I’ve spent MONTHS trying to get Shopify to understand that there are 2 fundamental issues with how Shopify manages the incoming inventory quantities from apps.
1. Incoming Inventory “Stuck” After App Uninstallation
When a merchant uninstalls an app, Shopify immediately severs all permissions, preventing the app from making any further changes. Although apps are required to delete their data upon uninstallation, any “incoming” inventory adjustments they created—whether positive or negative—remain on the product.
Because the app’s connection is terminated, it has no opportunity to clean up or “zero out” these leftover quantities. This leaves the inventory in a “stuck” state. The merchant is unable to resolve these lingering incoming inventory figures because the app that created them is no longer present to manage them. The only way for a merchant to fix this may be to reinstall the app solely for the purpose of having the app correct the inventory. This problem persists even if an app is behaving perfectly, as it is a limitation of the uninstall process.
2. Inability of Apps to “Zero Out” Their Own Incoming Inventory
The fundamental problem is that an app cannot isolate and remove only its own contribution to a product’s “incoming” inventory, a process referred to as “zeroing out”. This issue stems from the behavior of Shopify’s inventoryAdjustQuantities API mutation.
The key components of this issue are:
Net Total vs. App-Specific Inventory: The API only allows an app to see and modify the net total incoming inventory for a product. This net figure is the sum of all incoming sources, including Shopify Purchase Orders (POs) and any adjustments from apps.
Lack of Visibility: An app has no way to see the specific incoming quantity contributed by Shopify POs or other sources. It also cannot see its own true value stored by Shopify; it can only see the final net total.
The “Zeroing Out” Paradox:
When an app attempts to remove its influence, it uses the API to set the incoming quantity to 0.
However, the API interprets this as a command to set the net total incoming quantity to 0.
If a product already has incoming stock from a Shopify PO (e.g., +10 units), and the app sets the total to 0, Shopify forces the app’s contribution to become negative (-10 units) to make the math work (10 - 10 = 0).
Therefore, the act of setting the app’s value to “0” perversely results in it becoming a negative number. The app is then “stuck,” as it cannot know the correct value needed to truly offset its influence without being able to see the quantities from other sources. This behavior puts apps in an impossible situation where they cannot function correctly.
Thank you so much for sharing this! I’m going to dive it to make sure I fully understand.
Can I ask a few more questions to make sure I’m not missing anything here?
You seem to have a grasp of the current limitations. Is your post here primarily to report a bug, or is this more of a feature request/product gap that you’d like us to address?
Does this happen with incoming inventory to merchant managed locations or app managed locations?
I need a solution. Users are complaining because Horse can’t zero itself out.
Both
If used exclusively then it might. I haven’t tested it yet. But I am hopeful that if an app did use it exclusively then it could resolve the issues. However, it doesn’t help everyone else that still uses the existing API. As long as the existing API exists, then apps will still get into trouble with no way out.
I’ve added your questions 2 and 3 to the website.
If you found this helpful, please contact Natalie (manager), Paul (Shopify Technical Merchant Support), Matt (Plus Technical Merchant Support), and Chloe M (Shopify Technical Merchant Support) who continue to misunderstand the issue. They keep misunderstanding the issue and providing unhelpful solutions to different unrelated problems. They are VERY confused and could really benefit from speaking with someone that understands the issue.
I’ve been replicating this on my end to be sure I understand. This is what I’ve done and what I’ve found. Please let me know what I’m missing because it seems to work for me as expected and doesn’t get stuck, even after app uninstallation.
Spun up a custom app and used the adjustInventoryQuantities mutation to add incoming inventory to a newly created product
Used a separate custom app that has full permissions and the inventoryMoveQuantities mutation to move from incoming to available and the inventory looks to have moved.
There is absolutely a limitation on what merchants can do here though. The only places in the admin to adjust incoming inventory are through purchase orders and inventory transfers, but app managed inventory can only be managed by an app.
Right good work. So you needed to create a second app to remove that incoming inventory. Not possible for regular store owners. That’s issue 1. Store owners are left holding the bag after uninstalling an app. They are stuck because they can’t do this themselves.
You missed the part where issue 2 involves having separate incoming inventory. That separate incoming inventory can be from a Shopify Purchase Order (confirmed by me) or another app (unconfirmed but likely). When there is also incoming inventory from something else, then the app is “blind” when it just wants to zero out incoming. Re-read issue 2 to learn about the key concepts of “Net Total vs. App-Specific Inventory” and " Lack of Visibility"
NOTE: For issue 2, make sure not to use the knowledge that YOU have about how much inventory is incoming from a Shopify PO. YOU can see that but apps don’t. It’s kind of cheating to use that knowledge that an app is unable to know.
A work-around exists where a store owner could communicate with an app dev about how much is already incoming from Shopify. BUT, consider the case where we’re talking about hundreds of variants. So in that case it’s going to be A LOT of work for an app owner to communicate about ALL of those incoming quantities to an app dev. Not realistic, even as a work-around.
Finally note that this has already happened to me with 3 different store owners. So it’s a common issue. I, as a developer, can’t spend hours manually fixing everyone’s incoming inventory.
Question related to issue 3: Did you use the same ledgerDocumentUri for both apps? The ultimate test of this would be to try to delete the location that you used for testing
I’ve tested further to better understand issue #2, and you’re absolutely right about the problem.
I created a purchase order for 10 incoming units, then used inventoryAdjustQuantities to add 5 more from an app.
When I attempted to reduce the app’s contribution by removing 4 units, then 5 more, the app ended up with a negative contribution of -4 units while the total incoming inventory showed 6 units (10 from PO minus 4 from app).
The admin interface clearly shows this breakdown, but you have no way to query this information through the GraphQL API.
The solution here would be maintaining detailed records of every inventory adjustment you make. You need to track your net contribution per item/location to avoid going negative when attempting to zero out your inventory.
One option could be to store this in your own database, or if you don’t want to manage separate infrastructure, you can use metafields to store this tracking data.
Ultimately, this does seem to be the expected behavior currently since the pending PO incoming information is preserved, but that negative offset is definitely confusing and I’ll submit feedback about both issues to our product teams for consideration.
Just to clarify as I don’t think I tested this. If you uninstall and reinstall the app you can no longer re-adjust the inventory specific to your app and it will put it through as negative values?
The solution here would be maintaining detailed records of every inventory adjustment you make. You need to track your net contribution per item/location to avoid going negative when attempting to zero out your inventory.
Can’t do that if the user uninstalls.
Also what happens when your app has a bug? Mistakes happen and there needs to be a way to recover from them when they do.