medusajs/medusa · error · MedusaError

Inventory Level for Item ${id} at Location ${location_id} no

Error message

Inventory Level for Item ${id} at Location ${location_id} not found

What it means

The DELETE /admin/inventory-items/:id/location_levels/:location_id route queries for an inventory_level matching both the inventory item id and the location id; no match throws NOT_FOUND (HTTP 404).

Source

Thrown at packages/medusa/src/api/admin/inventory-items/[id]/location-levels/[location_id]/route.ts:29

import { HttpTypes } from "@medusajs/framework/types"
import { refetchInventoryItem } from "../../../helpers"

export const DELETE = async (
  req: MedusaRequest<{}, HttpTypes.AdminGetInventoryLocationLevelParams>,
  res: MedusaResponse<HttpTypes.AdminInventoryLevelDeleteResponse>
) => {
  const { id, location_id } = req.params

  const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)

  const result = await query.graph({
    entity: "inventory_level",
    filters: { inventory_item_id: id, location_id },
    fields: ["id", "reserved_quantity"],
  })

  if (!result.data.length) {
    throw new MedusaError(
      MedusaError.Types.NOT_FOUND,
      `Inventory Level for Item ${id} at Location ${location_id} not found`
    )
  }

  const { id: levelId, reserved_quantity: reservedQuantity } = result.data[0]

  if (reservedQuantity > 0) {
    throw new MedusaError(
      MedusaError.Types.NOT_ALLOWED,
      `Cannot remove Inventory Level ${id} at Location ${location_id} because there are reservations at location`
    )
  }

  const deleteInventoryLevelWorkflow = deleteInventoryLevelsWorkflow(req.scope)

  await deleteInventoryLevelWorkflow.run({
    input: {

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. GET the item's location levels first and delete only listed pairs
  2. Treat 404 on delete as already-deleted and continue idempotently
  3. Refresh the levels list after any delete
Defensive patterns

Strategy: try-catch

Validate before calling

const { inventory_item } = await sdk.inventoryItem.retrieve(itemId, { fields: ["location_levels.*"] })
const hasLevel = inventory_item.location_levels?.some((l) => l.location_id === locationId)
if (!hasLevel) throw new Error(`No level for ${itemId} at ${locationId}`)

Type guard

const levelExists = (levels: {location_id:string}[], locId: string) =>
  levels.some((l) => l.location_id === locId)

Try / catch

try { await deleteLevel(itemId, locationId) } catch (e) { if (e.statusCode === 404) { /* already deleted, success */ return } throw e }

Prevention

When it happens

Trigger: DELETE on an inventory item/location pair where no level exists — wrong location id, level already deleted, or item never stocked at that location.

Common situations: Double-click/retry of a delete that already succeeded; UI listing stale location levels; using a stock location id from a different store.

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/b74e027a9f6ff29e. Report an issue: GitHub.