Skip to content

Error Glossary

This page is a lookup reference for the errors the ShipHero Public API returns. Use it to go from the message and code you received to the reason the request was rejected and the change that resolves it.

Most failures are not specific to one operation. Before looking up an individual message, check Common causes — authentication, scopes, user permissions, 3PL account access and identifier handling account for the majority of rejected requests. The per-operation tables only document what is specific to that operation.

What is covered on this page:

Error response shape

GraphQL always answers with HTTP 200 when the query itself is valid. Failures are reported in the errors array, and the affected field is null in data. A single response can contain several errors.

{
  "errors": [
    {
      "message": "Order not found",
      "operation": "order_update",
      "field": "order_update",
      "request_id": "5da7dc13f3079f0def208711",
      "code": 5
    }
  ],
  "data": {
    "order_update": null
  }
}
FieldDescription
messageHuman-readable reason. This is the value you look up on this page.
operationThe top-level query or mutation that failed.
fieldThe field being resolved when the error was raised. Mutations report the operation name; queries usually report data, the wrapper field the resolver failed on.
request_idIdentifier for this request. Always capture it — support uses it to retrieve your exact payload.
codeNumeric error class. See Error codes. Not every error carries one.
detailsOptional array of { code, message } entries when one request failed several validations at once.

Throttling errors use a different set of fields: they carry required_credits, remaining_credits and time_remaining instead of field. See Throttling and rate limiting.

Note

Errors raised by GraphQL itself — unknown field, missing required argument, wrong argument type, malformed query — are rejected before your operation runs. They have no code, operation or request_id, and they are not catalogued on this page. See the GraphQL Primer if you are unsure whether a request is well formed.

mutation { order_create(data: {order_numberr: "SO-1001"}) { request_id } }
# → { "message": "Field 'order_numberr' is not defined by type 'CreateOrderInput'.",
#     "locations": [{ "line": 1, "column": 31 }] }   // no code, no request_id

Error codes

Codes follow the Google RPC code convention, with a few values added by the Public API.

CodeNameMeaningWhat to do
3Invalid ArgumentThe input is malformed, out of range, or a logically required field is missing.Correct the payload. Retrying the same request will fail again.
5Not FoundThe resource does not exist, or is not visible to the authenticated account.Query the resource first and use the identifier it returns, under the correct account. See the schema reference.
6Already ExistsThe resource or identifier you are creating already exists.Look up the existing record instead of creating it, or supply a unique identifier. See the schema reference.
7Permission DeniedA scope, a user permission, or access to the target 3PL account is missing.See Common causes.
9Failed PreconditionThe resource exists, but its current state does not allow the action.Move the resource into the required state, then retry.
10AbortedThe operation was aborted, typically because of a conflict.Read the resource to confirm its state, then retry.
14UnavailableA dependency needed to serve the request was temporarily unavailable.Retry with backoff.
20Format ErrorA value could not be parsed into the expected format.Check the types and formats of the values you sent.
21Service Not AvailableA downstream service timed out.Retry with backoff.
22Unexpected ErrorAn unhandled error occurred.Do not retry a mutation blindly. Read the resource first, then contact support with the request_id.
30Throttling ErrorThe operation costs more credits than your quota currently allows.See Throttling and rate limiting.

Codes 3, 5, 6 and 9 mean the request was understood and rejected: retrying it unchanged produces the same result. For errors with codes 10, 14, 21 and 22, the API automatically refunds credits reserved for the failed request. A retry is a new request and is subject to normal credit accounting.

Warning

A server-side error on a mutation does not guarantee the mutation did not take effect. Before retrying a mutation that returned code 10, 14, 21 or 22, query the resource to check whether the change was already applied.

# after a code 22 on order_update, read before you retry
query { order(id: "T3JkZXI6MTIzNDU2") { data { order_number fulfillment_status } } }

Throttling and rate limiting

Two independent limits can reject a request, and they fail in different ways.

Credit quota returns GraphQL error code 30 with required_credits, remaining_credits and time_remaining. Whether waiting helps depends on which limit you hit:

  • The operation fits your account maximum but not your current balance — wait time_remaining and retry.
  • The operation costs more than your account may spend on a single request — waiting never helps, because the balance can never grow that large. Reduce the request: select fewer fields, lower first, and page through the results.

The decision logic and worked examples are in Example 3: Handle an insufficient-credit error; costs and optimization are covered under Throttling & Quotas.

Request-rate limit is separate and is not a GraphQL error at all. Exceeding 7,000 requests in any five-minute window returns HTTP 429 Too Many Requests with no errors array and no request_id. Failed and introspection requests count toward it. Back off until the window clears — see Request-rate limit.

Note

If you are handling errors by reading errors[0].code, a 429 has no body to read. Check the HTTP status before parsing a GraphQL error response.

POST /graphql
# → HTTP 429 Too Many Requests, empty body — nothing to parse as a GraphQL error

Common causes

These causes are not tied to a single operation. They explain most errors, so they are documented once here rather than repeated in every table below.

Authentication

A missing or malformed Authorization header is rejected before GraphQL runs, with HTTP 401 and no error code.

MessageCauseSolution
Authorization header is expectedNo Authorization header was sent.Send Authorization: Bearer <access_token>.
Authorization header must start with 'Bearer'The scheme is missing or not Bearer.Use the Bearer scheme exactly.
Token not foundThe header contains Bearer with no token after it.Include the access token.
Authorization header must be Bearer tokenThe header has extra parts beyond scheme and token.Send exactly two space-separated values.

A token can also be well formed and still be rejected because of the state of the user or the account. These are HTTP-level responses with a plain message and no error code.

StatusMessageCauseSolution
403Public API access is disabled for this userAPI access is turned off for this user.You cannot turn this on yourself. Ask an account admin to enable it, following How to Give a ShipHero User API Access; the steps are also in Public API access is disabled for this user.
401Account is canceledThe ShipHero account is canceled.Nothing in the API will change this. Ask the account owner to reactivate the account; if you do not know who that is, ask in the ShipHero Community.
401Trial account is expiredThe trial period ended.Nothing in the API will change this. Ask the account owner to move the account onto a paid plan, or ask in the ShipHero Community who to contact.
401Access to the Public API is not available on the ShipHero Essential planThe plan does not include Public API access.Nothing in the API will change this. Ask the account owner which plan includes API access, or ask in the ShipHero Community.

Note

These four are rejections of the user or the account, not of your request, so no change to the query will resolve them. They all need someone with access to the ShipHero account to act. If you are an external developer and do not know who that is, ask in the ShipHero Community rather than retrying.

Scopes

Tokens issued through the OAuth flow carry scopes, and an operation fails with code 7 when the token is missing one.

MessageCauseSolution
Missing required scope(s): <scope>The token does not include every scope the operation requires.Re-authorize requesting the listed scopes. Queries need view:, mutations need change:.
User does not have permissions to access [<resources>]The user behind the token is not granted Public API access to the listed resource. This is checked against the user’s own API permissions, not against the token’s scopes, so requesting a new token will not fix it.Ask an account admin to give the user API access to that area — see How to Give a ShipHero User API Access — or ask in the ShipHero Community.

Some operations need more than one scope. Creating an inbound shipment from an ASN, for example, requires both change:inbound_shipments and change:purchase_orders. See Scopes for the full list.

Note

Tokens generated through https://public-api.shiphero.com/auth/token do not enforce scopes. If you get a scope error, you are using an OAuth token.

User permissions

A handful of operations also check whether the ShipHero user behind the token holds a product permission, on top of the token’s scopes: user_update (users.manage.update), warehouse_profile_create (warehouses.manage.create), bill_add_ad_hoc_charge (billing.create), bill_update_ad_hoc_charge (billing.update), and the Experimental billing_charges and billing_rates (billing.manage.read).

Note

Developer users skip this check. If you authenticate with a developer user, you will not see this error on the operations above. It only reaches regular ShipHero users calling the API. The one place a developer user is hard-blocked is Operations gated by internal permissions, where a separate service does its own check.

Scopes are checked before permissions, so a token missing the scope reports Missing required scope(s): … first and never reaches the permission check.

MessageCauseSolution
User has no permission for resourceReturned by the operations above when a regular user lacks the product permission.If you administer the account, grant the permission under Users & Roles (how to do it). If you do not, ask an account admin, naming the operation you are calling. If nobody can identify the permission, ask in the ShipHero Community.

3PL account access

3PL users act on a customer account by passing customer_account_id. Operations reject an account the token cannot reach with code 7.

MessageCauseSolution
User cannot act on behalf of this accountThe customer_account_id is not a child account of the authenticated user.Pass an account the token is authorized for. List them with account.
Cannot create an order for this accountThe account in the order payload is not reachable by this token.Use an authorized customer_account_id.
Cannot access <resource>The record exists but belongs to an account the token cannot reach.Confirm you are querying under the right account.
Cannot access the requested <resource>. Permission deniedA mutation was sent with a customer_account_id the token is not authorized for. <resource> is the record type, or the literal word resource when none is named. This one arrives as code 9.Pass an account the token can reach. List them with account.

Note

An unauthorized customer_account_id produces two different codes depending on which operation you call, not on how wrong the account is. Operations that check it explicitly — cycle counts, inbound shipments, totes and the Experimental ASN operations — answer User cannot act on behalf of this account with code 7. Everywhere else the rejection comes from deeper in ShipHero and arrives as Cannot access the requested <resource>. Permission denied with code 9. Treat both as the same mistake.

mutation { cycle_count_items_create(data: {customer_account_id: "QWNjb3VudDox", ...}) { request_id } }
# → { "message": "User cannot act on behalf of this account", "code": 7 }

mutation { product_update(data: {sku: "ABC", customer_account_id: "QWNjb3VudDox"}) { request_id } }
# → { "message": "Cannot access the requested resource. Permission denied", "code": 9 }

Note

Cannot access <resource> and <resource> not found (code 5) are different answers. “Not found” means no such record exists; “cannot access” means it exists under an account you may not read. Both are returned by inventory, returns, wholesale and LPN operations.

The code on an access error is not always 7. Operations that raise it directly return code 7, but the same kind of rejection raised deeper in ShipHero arrives as code 9, as Cannot access the requested order. Permission denied does. Do not treat code 7 as the only sign of an access problem.

query { product(sku: "NOT-A-SKU") { data { sku } } }
# → { "message": "Product not found", "code": 5 }        // no such record
# → { "message": "Cannot access Location", "code": 7 }    // exists, other account

Warning

An account problem does not always produce an error at all. List queries such as products and orders answer a customer_account_id the token cannot reach with an empty page and no error, exactly as they would for an account that genuinely has no records. If a list comes back empty when you expect data, confirm the customer_account_id and the account the token belongs to before concluding there is nothing there. Single-resource lookups such as product and order do return <resource> not found (code 5) in the same situation.

query { products(customer_account_id: "QWNjb3VudDox") { data(first: 1) { edges { node { sku } } } } }
# → { "data": { "products": { "data": { "edges": [] } } } }   // no errors array at all

Account types

Several errors on this page depend on what kind of account the token belongs to, and no query tells you which kind it is.

Account typeWhat it means for the API
BrandOwns its own products, inventory and warehouses. customer_account_id does not apply.
3PL parentOwns the warehouses and acts for client accounts by passing customer_account_id.
3PL clientIts products and orders live under its own account, but the warehouses belong to the 3PL. A client account normally has no warehouses of its own, so operations that need a warehouse fail unless the account is set up as a hybrid.
HybridA client account that also operates its own warehouses.

This matters most for anything that takes a warehouse_id: on a 3PL client the warehouse belongs to the 3PL, not to the account you are authenticated as. If you are unsure which type you are integrating against, ask the people who contracted the warehouse, or email support@shiphero.com. Guessing from an empty warehouse list is not reliable, because an empty list can also mean an access problem. For help identifying your setup, ask in the ShipHero Community.

Identifiers

Most Invalid id and “not found” errors come from constructing identifiers by hand.

MessageCauseSolution
Invalid id <id>The value is not a well-formed ShipHero identifier.Use an identifier returned by a query, unmodified. The schema reference lists the lookup query for each resource.
Invalid idsAn id field was sent with nothing usable in it: an empty list, or null. A badly formed identifier returns Invalid id <id> instead.Send at least one identifier returned by a query. The schema reference lists the lookup query for each resource.
Missing or invalid field: '<field>'A field that is logically required for this input combination is absent or unusable.Supply the field named in the message.
One of the fields <field> of type 'String!' is required.A group of mutually dependent fields was left incomplete.Provide the field named in the message.

Public API identifiers are Base64 strings that encode an entity type and a number, for example T3JkZXI6MTIzNDU2. Treat them as opaque: do not decode, edit or rebuild them, and do not compare them numerically. To turn a numeric legacy_id into one, use the uuid query rather than encoding it yourself. See Core concepts.

Note

Queries do not report a bad identifier as a format problem. An identifier that is not valid Base64, encodes the wrong entity type, or belongs to another account all return <resource> not found (code 5). So “not found” does not always mean the record is missing — check the identifier before concluding the data is gone. The Invalid id errors below come from mutations, which convert identifiers before running.

query { order(id: "12345") { data { order_number } } }              # not Base64
query { order(id: "UHJvZHVjdDoxMjM=") { data { order_number } } }   # Base64 of Product:123
# → both: { "message": "Order not found", "code": 5 }

Scope warnings are not errors

When a token lacks the scope required for an individual field, the request still succeeds. The field resolves to null and a warning is reported under extensions.scope_warnings, keyed by <ParentType>.<field_name>.

{
  "data": {
    "order": {
      "id": "T3JkZXI6MTIzNDU2",
      "email": null
    }
  },
  "extensions": {
    "scope_warnings": {
      "Order.email": {
        "code": "FIELD_SCOPE_MISSING",
        "message": "Field 'email' was not resolved because scope(s) 'view:pii' are missing",
        "field_id": "Order.email",
        "parent_type": "Order",
        "field": "email",
        "required_scopes": ["view:pii"],
        "occurrences": 1
      }
    }
  }
}

occurrences counts how many times the field was blocked across the response, so a blocked field inside a list is reported once rather than per item.

Read required_scopes and re-authorize with those scopes if you need the data. If a field you expected comes back null and no error was returned, inspect extensions.scope_warnings before treating it as missing data.

Authentication endpoint errors

The token endpoints are plain HTTP endpoints, not GraphQL. They take a POST with a JSON body and answer with an HTTP status code and a bare JSON string as the body — there is no errors array, no code and no request_id to read.

EndpointMessageSolution
/auth/tokenusername is requiredInclude username in the body.
/auth/tokenpassword is requiredInclude password in the body.
/auth/tokenService UnavailableThe identity provider did not answer. Retry with backoff.
/auth/refreshrefresh_token is requiredInclude refresh_token in the body.
/auth/refreshService UnavailableThe identity provider did not answer. Retry with backoff.

See Authentication for the full request format.

Messages by operation group

Messages specific to each group of operations. Causes that apply everywhere are in Common causes and are not repeated here.

Orders

CodeMessageReturned bySolution
3Invalid input. Details: country is requiredorder_create and order_update. The shipping or billing address has no country.Send a two-letter ISO country code. See Country or State codes.
3Invalid order. Details: <errors>order_create. One or more order fields failed validation.Fix each field listed in the message.
3Invalid input. Details: Missing shipping_lines.carrier, Missing shipping_lines.methodorder_update. An order was sent with shipping_lines but without both a carrier and a method.Send both together, for example carrier: "UPS" and method: "Ground".
3At least one line item is requiredorder_create. line_items was empty.Include at least one line item.
3Invalid line items. Details: <errors>order_add_line_items and order_update_line_items. One or more line items failed validation.Fix each item listed in the message.
3Invalid input. Details: cancel_on_platform and void_on_platform are only valid when fulfillment_status is exactly 'canceled'order_update_fulfillment_status. Either flag was sent with another fulfillment_status.Set fulfillment_status to canceled, or remove both flags.
3Need to specify at least one line item idorder_remove_line_items. A line item operation received no ids.Send the ids of the line items to act on.
3Need to specify at least one line itemorder_update_line_items. A line item operation received no items.Send at least one line item.
3Need to specify old and new skuorder_set_line_item_sku. A SKU replacement was sent with only one SKU.Send both the current and the replacement SKU.
3Invalid History Entry. Details: <errors>order_add_history_entry. An order history entry failed validation.Fix the fields listed in the message.
5Order not foundorder_set_line_item_sku and order_add_attachment. No order matches the identifier under this account. For order_set_line_item_sku, this also covers an identifier that is malformed or belongs to another entity type.Look the order up with orders and use the id it returns.
9Cannot access the requested order. Permission deniedAny order mutation. It is raised in ShipHero’s own order layer rather than by a single operation. The order exists but belongs to an account this token cannot reach. Note the code is 9, not the 7 you would expect from the wording.Use a token for the account that owns the order, or pass an authorized customer_account_id. See 3PL account access.
9Order with order_number '<order_number>' already existsorder_create. An order with that order_number is already in the account.Use a unique order_number, or update the existing order.
9SKUs not found: <skus>order_create. The listed SKUs do not exist in the account.Create the products first, or correct the SKUs.
9Duplicate partner_line_item_id not allowed: <ids>order_create. The listed partner_line_item_id values repeat within the order or already exist.Make each partner_line_item_id unique.

Note

An order_create that is rejected for a duplicate order number, an unknown SKU or a repeated partner_line_item_id comes back as code 9, not the code 6 you would expect from “already exists”. If your integration decides what to do from the code alone, read the message too.

mutation { order_create(data: {order_number: "SO-1001", ...}) { request_id } }
# → { "message": "Order with order_number 'SO-1001' already exists", "code": 9 }

Order fulfillment, merging and tracking flows are described in Order Fulfillment and Merge Orders.

Products

CodeMessageReturned bySolution
3Sku cannot be emptyproduct_create. sku was absent or blank.Send a non-empty SKU. Confirm it with products.
3Missing or invalid image urlproduct_create, product_delete_image and product_update. An image entry is null, or its src is empty or only whitespace. The field is called src even though the message says “url”.Send a non-empty src on every image. Nothing else about the value is validated, so an unreachable address is accepted here and simply fails to display.
3Missing or invalid image case_barcodeproduct_create and product_update. A case pack entry is null, or its case_barcode is empty or only whitespace. This message mentions “image” but refers to a case pack.Send a non-empty case_barcode. No format or length rule is applied.
3Missing or invalid image case_quantityproduct_create and product_update. A case pack entry’s case_quantity is 0 or absent. This message mentions “image” but refers to a case pack.Send a quantity other than 0.
3Tag values must be stringsproduct_update. A tag was sent as a number, boolean or object.Send every tag as a string. See Removing commas from tags.
5Product not foundThe product query cannot find the SKU under the requested account.See Product not found under Inventory.
5Sku '<sku>' not foundproduct_update could not find that SKU under the account it searched. Without customer_account_id it searches the token’s own account, so a 3PL gets this for a SKU that exists under a client. An empty sku reports as Sku '' not found, not as a missing-field error.If the product belongs to a 3PL client, resend with that client’s customer_account_id. Otherwise confirm the SKU with products.
5Sku '<sku>' not found in warehousewarehouse_product_update found no record for that SKU in the given warehouse.Confirm the SKU is stocked in that warehouse with warehouse_products.
5Product <sku> not found for account <account_id>warehouse_product_update. The SKU does not exist under the account named in the message. This is the only product lookup error that tells you which account was searched; the others leave it implicit.Read the account id in the message. If it is your 3PL parent account and the product belongs to a client, resend with that client’s customer_account_id. Otherwise confirm the SKU with products.

Note

A null list is different from a null entry: product_update accepts images: null and cases: null, but images: [null] and cases: [null] return the validation errors above. Sending images: null leaves existing images unchanged; it does not delete them.

Inventory, locations and LPNs

CodeMessageReturned bySolution
3Invalid skuinventory_replace. sku is empty or only whitespace. The value itself is not validated against existing products here.Send a non-empty SKU.
9One of the following fields is required: location_id or lpn_idinventory_add, inventory_remove, inventory_replace and inventory_subtract. Neither a location nor an LPN was given.Send exactly one of the two.
9One of the fields <field> of type 'String!' is required.inventory_transfer. A move is missing its source or destination. The message names the field it needs.Send the field named in the message.
3Location name cannot be emptylocation_create. name was blank on a location create or update.Send a non-empty name.
3Location zone cannot be emptylocation_create. zone was blank.Send a non-empty zone.
3Invalid container_type. Must be one of: BOX, PALLET, STAGINGlicense_plate_numbers_create. An unsupported container_type was sent for an LPN.Use one of the listed values.
3Quantity must be between 1 and 1300license_plate_numbers_create. The quantity is outside the accepted range.Split the operation into several requests.
3Invalid idsitem_location_delete. The ids list was sent empty, or sent as null. A badly formed id returns Invalid id <id> instead.Send at least one item location id from item_locations. Each must already have quantity 0.
5Location not foundinventory_transfer. No location matches the identifier in this warehouse.Look it up with locations and use a returned id.
5LPN not foundinventory_add, inventory_remove, inventory_replace, inventory_subtract and inventory_transfer. No LPN matches the identifier.Look it up with license_plate_numbers and use a returned id.
5Product not foundinventory_add, inventory_remove, inventory_subtract and inventory_transfer. The SKU does not exist under this account. For a 3PL, check whether the SKU belongs to a client account and needs customer_account_id.Confirm the SKU with products, then create the product or correct the SKU.
5Product within the location for the given warehouse not foundinventory_transfer. There is no inventory record for that SKU in that location and warehouse. As above, omitting customer_account_id makes the lookup use the token’s own account, so a 3PL can get this for stock that is really there under a client.Confirm the stock with warehouse_products, and pass the client’s customer_account_id if the inventory belongs to a 3PL client.
9Unable to delete locationlocation_delete. The location still has item locations attached.Move or remove the inventory, then delete the location.
9... staging location for wholesale order ...location_delete. The location is reserved as a staging location for a wholesale order.Resolve the wholesale order first. See Staging Locations.
7Cannot access Location / Cannot access LPNthe inventory_* mutations and return_receive_line_item. The record belongs to an account the token cannot reach.See 3PL account access.
7Cannot access the product within the location for the given warehouseinventory_transfer. The inventory record belongs to an unreachable account.See 3PL account access.

location_bulk_create does not fail on duplicate names. It reports them in skipped_count and skipped_names, so check both fields rather than assuming every location was created. Full walkthrough in Inventory.

Returns

CodeMessageReturned bySolution
3quantity_received must be > 0return_receive_line_item. A received quantity of zero or less was sent.Send a positive quantity.
3restock_quantity must be >= 0return_receive_line_item. A negative restock quantity was sent.Send zero or more.
3restock_quantity cannot exceed quantity_receivedreturn_receive_line_item. More units were restocked than were received.Restock at most the received quantity.
3bin_id is required when restocking inventoryreturn_receive_line_item. A restock was requested without a destination bin.Send the bin_id the units go into.
5Invalid warehouse_id / Invalid lot_id / Invalid bin_idreturn_receive_line_item. No warehouse, lot or bin matches the identifier under this account. Invalid warehouse_id is also returned by product and inventory mutations.Use an identifier from account or the matching lookup query.
5Return not foundreturn_create_exchange and return_receive_line_item. No return matches the identifier under this account.Look it up with returns and use a returned id.
5return_item not found in returnreturn_add_attachment and return_receive_line_item. The item id does not belong to that return.Use an item id taken from the return itself.
5Product not foundreturn_receive_line_item. The SKU on the return item does not exist under the requested account.See Product not found under Inventory.
3Bin does not belong to the given warehousereturn_receive_line_item. The bin and the warehouse do not match.Use a bin from the same warehouse.
9Return label generation requires a user with an assigned warehouse.return_create. The authenticated user has no default warehouse.Assign a warehouse to the user, or authenticate as a user who has one.
3Invalid value <value> in field label_costreturn_create. label_cost is not a valid decimal.Send a decimal number, not a formatted string.
7Cannot access Warehouse / Cannot access Product / Cannot access Locationinventory_transfer and return_receive_line_item. The record belongs to an unreachable account.See 3PL account access.

Return flows are described in Returns.

Shipments and shipping containers

CodeMessageReturned bySolution
3Must provide at least one shipping container IDshipping_container_delete and shipping_container_print_barcode_labels. The id list was empty.Send at least one container id.
3Specify exactly one of shipping_container_id or shipping_container_barcodeshipping_container_add_shipping_label and shipping_container_remove_shipping_label. Both or neither identifier was sent.Send exactly one.
3Container quantity must be at least 1shipping_container_create. A quantity below 1 was sent.Send 1 or more.
3Cannot create more than 100 shipping containersshipping_container_create. More than 100 containers in one request.Split into batches of 100.
3Cannot delete more than 100 shipping containersshipping_container_delete. More than 100 ids in one delete.Split into batches of 100.
3Cannot print more than 100 shipping container barcode labelsshipping_container_print_barcode_labels. More than 100 labels in one request.Split into batches of 100.
3Invalid value <value> in field costshipment_create and shipment_create_shipping_label. cost is not a valid decimal.Send a decimal number.

Purchase orders and shipping plans

CodeMessageReturned bySolution
3Invalid line_items: null items are not allowedpurchase_order_create. The line_items list contains a null entry, such as line_items: [null].Remove null entries and send a valid line item object for each item.
3Invalid value <value> in field <field>the purchase_order_*, shipment_*, return_create and shipping_plan_create mutations. A decimal field received a value that cannot be parsed.Send a plain decimal, without currency symbols or thousands separators.
7Only ShipHero Fulfillment accounts can create shipping plans.shipping_plan_create is restricted to a ShipHero-operated fulfillment account. Standard accounts cannot create shipping plans at all, so no change to the request will work.Not something the API can grant. Ask in the ShipHero Community or email support@shiphero.com to find out whether your account can use this operation.
7Shipping plans can only be created for ShipHero Fulfillment warehouses.shipping_plan_create was pointed at a warehouse that is not one the operation accepts.Not something the API can grant. Ask in the ShipHero Community or email support@shiphero.com.

See Purchase Orders.

Cycle counts

CodeMessageReturned bySolution
3Invalid sort_by '<value>'. Supported values: <values>cycle_count_items_create, cycle_count_locations_create and cycle_count_recount_create. An unsupported sort field was requested.Use one of the listed values.
3'skus' and 'item_filters' are mutually exclusive — provide one or the other, not both.cycle_count_items_create. Both filters were sent in one request.Send only one filter.

See Cycle Counts.

Totes

CodeMessageReturned bySolution
5Tote not foundclear_tote, tote_clear, tote_delete, tote_transfer_to_location and tote_update. No tote matches the identifier.Look it up with totes and use a returned id.
5Location not foundtote_transfer_to_location. No location matches the identifier.Look it up with locations and use a returned id.
9Tote and location must belong to the same warehousetote_transfer_to_location. The tote and the destination are in different warehouses.Use a location in the tote’s warehouse.
9Tote has no current picks to transfertote_transfer_to_location. The transfer mutation was called on a tote with nothing picked into it.Check the tote first with tote: picks lists what is in it, picked_quantity counts the units, and status is Empty when there is nothing to transfer.

Lots

CodeMessageReturned bySolution
5Lot not foundlot_assign_to_location. No lot matches the identifier.Look it up with expiration_lots and use a returned id.
5Location not foundlot_assign_to_location. No location matches the identifier.Look it up with locations and use a returned id.

See Lots.

Wholesale orders

CodeMessageReturned bySolution
3Invalid line_items: null items are not allowedwholesale_order_create. The line_items list contains a null entry, such as line_items: [null].Remove null entries and send a valid line item object for each item.
7Cannot create an order for this accountwholesale_order_create. The token cannot create an order for the supplied customer_account_id.Use an authorized customer_account_id. See 3PL account access.
9Order contains regular kits, convert regular kits to build kits before proceedingwholesale_order_create. A line item refers to a regular kit rather than a build kit.Convert the regular kit to a build kit before creating the wholesale order.
3Missing or invalid order_idthe wholesale_* operations. order_id is absent or malformed.Send an order_id from wholesale_orders.
5Wholesale line items not found: <ids>wholesale_order_update. The listed line item ids do not belong to the order.Use ids taken from the order itself.
5Wholesale order not foundthe wholesale_* operations. No wholesale order matches the identifier.Look it up with wholesale_orders and use a returned id.
5The requested resource picking_job does not existwholesale_set_as_ready_to_pick. No picking job was found for the order’s pending shipment. This refers to an internal picking resource, not the order identifier.Check the wholesale order’s allocation and picking state in the app. If the order appears ready but the error persists, contact support@shiphero.com with the order id and request id.
9You cannot select a staging location that is already in usewholesale_order_update_staging_location. The selected staging location is already assigned to a wholesale order.Select an unused staging location, or resolve its existing assignment in the app before retrying. See Staging Locations.
3Wholesale order has multiple or no pending shipmentswholesale_order_auto_allocate_for_picking, wholesale_order_fulfill, wholesale_order_generate_labels and wholesale_order_transfer_picks_to_staging. The operation acts on a single shipment, and the order has none pending or more than one.Check the order’s shipments with wholesale_order. Changing which shipments are pending is done in the app, not the API — see Making Changes to Wholesale Orders.
9Cannot edit the outbound schedule of a fulfilled orderwholesale_order_edit_outbound_schedule, once the order is fulfilled.The schedule is fixed at fulfillment and cannot be reopened through the API. See Outbound Schedule and Order Completion for Wholesale.
9Cannot print packing slip for this orderwholesale_order_print_packing_slip. The wholesale order has not reached a state that produces a packing slip.Packing slips exist only once the order is packed. See How to Print Packing Slips and Generate Bills of Lading for Wholesale.
9The order is not ready to print, warehouse completed or fulfilledwholesale_order_print_labels. Labels were requested before the wholesale order reached a printable state.Finish picking and packing first, then print.
7Cannot access wholesale orderthe wholesale_* operations. The order belongs to an unreachable account.See 3PL account access.

See Wholesale.

Work orders

CodeMessageReturned bySolution
3Missing input parameters, assembly_sku is required for Work Orders of type Assemblywork_order_create. An Assembly work order was created without assembly_sku.Send the SKU of the assembled product. See Assembly Products.
9File too large for <filename>: <detail>work_order_create. An attachment exceeds the allowed size.Reduce the file size and retry.
9Invalid file URL for <filename>work_order_create. ShipHero refused to fetch the attachment. Same safety check as the invoice logo: the URL was rejected before any download: it must be https, must have a hostname that resolves, and must not resolve to a private or internal address.Host the file at a public https URL.
9Too many redirects for <filename>work_order_create. The attachment URL redirects too many times.Link the final URL directly.
9Error while processing file <filename>work_order_create. The attachment was fetched but could not be processed.Confirm the file is valid and not corrupt.

Users

CodeMessageReturned bySolution
3Invalid default_warehouse_iduser_update. The id is well formed but no warehouse matches it, or the warehouse is not accessible to the account the user belongs to. When customer_account_id is sent, the warehouse is checked against that account, not against the token’s. A badly formed id returns Invalid id <id> instead.Use a warehouse that belongs to the same account as the user you are updating. For a 3PL updating a client’s user, that means one of the client’s warehouses, not the parent’s.
3Invalid default_order_statususer_update. The value is not one of the custom order statuses configured on that account. These are not a fixed schema enum, so the accepted values differ from account to account.Use one of the order statuses configured on the account you are updating, as they appear in the ShipHero app.
3Cannot set default_warehouse_id, default_order_status or default_pick_order_tag on this useruser_update. The user being updated has no warehouse app login. All three fields only exist for users who sign in to the warehouse app.Set them only on users that have a warehouse app login.

Warehouse profiles

CodeMessageReturned bySolution
3<address>.<field> cannot be emptywarehouse_profile_create. A required address field is blank.Fill in the field named in the message.
3profile cannot be emptywarehouse_profile_create. The profile name is blank.Send a non-empty name.
3profile cannot exceed 40 characterswarehouse_profile_create. The profile name is too long.Shorten it to 40 characters or fewer.
3profile must be a non-default warehouse profilewarehouse_profile_create. The operation targeted the default profile.Target a non-default profile.
3manual_order_prefix cannot exceed 10 characterswarehouse_profile_create. The prefix is too long.Shorten it to 10 characters or fewer.
3invoice_email is invalidwarehouse_profile_create. The address fails a syntax check. Deliverability is deliberately not checked, so a well-formed address that does not exist is accepted.Send a syntactically valid address.
9Invalid invoice_logo_urlwarehouse_profile_create. ShipHero refused to fetch the URL. This is a safety check, not a format one: the URL was rejected before any download: it must be https, must have a hostname that resolves, and must not resolve to a private or internal address.Host the logo at a public https URL. A URL that is reachable but fails to download reports Unable to download invoice logo instead.
9Invoice logo cannot exceed 5 MBwarehouse_profile_create. The image is too large.Supply an image of 5 MB or less.
9Invoice logo must be a PNG or JPEG imagewarehouse_profile_create. The image is another format.Convert it to PNG or JPEG.
9Invoice logo URL has too many redirectswarehouse_profile_create. The URL redirects too many times.Link the final image URL.
9Unable to download invoice logowarehouse_profile_create. The image could not be fetched.Make sure the URL is publicly reachable.
5Physical warehouse not foundwarehouse_profile_create. The warehouse does not exist under this account.Use a warehouse id from account.

Experimental API

Operations on the Experimental API return the same codes. These messages are specific to it. For identifier and cross-account access errors, see Identifiers and 3PL account access.

CodeMessageReturned bySolution
9Feature not enabledinbound_shipment_create_from_asn. The account is not enabled for this Experimental operation. Access is granted per account, not per token, so no scope or credential change will help.Email support@shiphero.com to have it enabled for the account, or ask in the ShipHero Community.
—Experimental GraphQL schema access is not enabled/graphql/experimental itself, as HTTP 403, before the query runs, when the account is not enabled for the Experimental endpoint at all. It has no GraphQL errors array and no code. Enablement is per account.Email support@shiphero.com to have the account enabled. Until then every Experimental operation returns this, regardless of scopes.
7User cannot act on behalf of this accountthe cycle_count_*, inbound_shipment_* and tote_* mutations, the only ones that check customer_account_id explicitly. The account is not one the token may act for.See 3PL account access.
7TPL billing is not enabledbilling_charges and billing_rates. Billing queries were called on an account without 3PL billing.3PL billing is an account setting, not an API one. Ask an account admin to enable it — see 3PL Billing for what it covers — or ask in the ShipHero Community.
3date_from must be a valid ISO-8601 datetimelabor_worker_activities. The date could not be parsed.Send an ISO-8601 datetime. See Dates.
3date_from must be between 14 days before and <n> hours after the current UTC timelabor_worker_activities only serves the last 14 days. Splitting the range into smaller windows does not reach further back.For anything older, use the LABOR module of Data Exports, which covers activities, workers, jobs and warehouses.
3started_at/finished_at must be a valid ISO-8601 datetimelabor_activity_start and labor_activity_modify. A labor activity timestamp could not be parsed.Send an ISO-8601 datetime.
3finished_at requires started_atlabor_activity_start and labor_activity_modify. An end time was sent with no start time.Send both, or neither.
3finished_at must be later than started_atlabor_activity_start and labor_activity_modify. The interval is inverted.Order the timestamps correctly.
3<timestamp> cannot be in the futurelabor_activity_start and labor_activity_modify. A labor activity timestamp is in the future.Send a past or current timestamp.
3first must be between 1 and <max>labor_worker_activities. first is below 1 or above the maximum, which is 100 for these operations.Request 100 or fewer and page with the cursor.
3Invalid cursorlabor_worker_activities. The cursor is not a value this operation issued. Cursors do not expire, so this is a format problem, not a stale-page one.Pass a cursor exactly as the previous page returned it, or omit it to start from the first page.
3Activity <id> is not valid for this operation.labor_activity_remove. The activity cannot be used for this operation.Confirm the activity id and its state.
3activity_id is not valid for this account.labor_activity_finish and labor_activity_modify. The activity belongs to another account.Use an activity from the authenticated account.
3Worker <id> is not valid for this activity.labor_activity_start. The worker is not assignable to the activity.Use a worker returned for that activity.
3Job <id> is not valid for this activity.labor_activity_start. The job is not assignable to the activity.Use a job returned for that activity.
9The activity has already been finished.labor_activity_finish. The activity is already closed.Finished activities cannot be changed.
9This activity cannot be removed because it is active. Please finish the activity before proceeding.labor_activity_remove. The activity is still running.Finish it, then remove it.
9Activity conflicts with existing Workforce Hero activity data.labor_activity_finish and labor_activity_start. The interval overlaps an existing activity.Adjust the interval so it does not overlap.
7User does not have permission to manage Workforce Hero activities.labor_activity_finish, labor_activity_modify, labor_activity_remove and labor_activity_start. The user lacks the Warehouse Hero permission.See Operations gated by internal permissions.

Note

labor_worker_activities only accepts workers that are currently active in Workforce Hero. An inactive worker cannot be queried at all, and that is an upstream Workforce Hero limitation rather than something a different request shape can work around. The same operation pages at most 100 records and reads timestamps as UTC.

Warning

Data is temporarily unavailable. Please try again later comes back with code 14 from the labor mutations and code 9 from the labor queries. Despite the wording it is also what Workforce Hero returns when it rejects the request on permission grounds, so a failure that repeats is usually not an outage. Two things to check, in order: that the user is a regular ShipHero user holding the Warehouse Hero permission rather than a developer user (see Operations gated by internal permissions), and that Workforce Hero is active for the warehouse — that one is enabled by support@shiphero.com, not through the API.

Note

The labor queries return an empty page — not an error — when Workforce Hero does not recognize the warehouse. An empty result does not confirm the warehouse has no data; confirm the warehouse_id is one Workforce Hero tracks.

Carrier errors on label operations

The Experimental API operations that create, quote and void shipping labels call carriers directly. When a carrier rejects a request, the text in the response is the carrier’s own, not ShipHero’s: it can contain JSON, field paths, carrier error codes and provider-specific wording. Treat it as opaque text to surface or log, never as a stable string to branch on.

These errors come from the carrier’s systems, so the codes and common causes above do not classify them. What matters is which channel a given operation reports them through.

OperationScopeHow carrier problems are reported
label_quoteview:shipmentsAs data, in errors, warnings and carrier_errors
label_printchange:shipmentsAs a GraphQL error, with the carrier’s text as the message
label_voidchange:shipmentsAs a GraphQL error

Quoting rates

label_quote reports carrier problems as data rather than as GraphQL errors, across three fields:

  • errors — top-level quote errors.
  • warnings — non-blocking warnings; rates are still returned.
  • carrier_errors — per-carrier errors, each entry carrying the carrier and its list of errors.

The include_partial_rates input decides whether a partial failure is fatal. It defaults to false:

  • false — if a carrier fails, the mutation fails. You get a GraphQL error and no rates.
  • true — successful rates are returned in rates and the failures are reported in carrier_errors alongside them.

Set include_partial_rates: true when quoting several carriers and a usable rate is better than none. A successful response can then still contain carrier failures, so check carrier_errors even when rates is populated.

Warning

With include_partial_rates: true, a quote that partially failed returns HTTP 200 with no errors array at the GraphQL level. Code that only inspects the top-level errors array will treat it as a clean success and silently miss that a carrier was unavailable.

mutation { label_quote(data: {order_id: "T3JkZXI6MTIzNDU2", include_partial_rates: true, ...}) {
  rates { carrier cost } carrier_errors { carrier errors } } }
# → HTTP 200, rates populated, and carrier_errors: [{ "carrier": "ups", "errors": ["..."] }]

Printing and voiding labels

label_print returns labels and weight_discrepancies. It has no field for carrier errors, so a carrier rejection surfaces as a GraphQL error whose message is the carrier’s raw text. The same applies to label_void, whose label_id must be an outbound ShippingLabel: return and RMA labels are rejected.

Important

This section covers only the Experimental label_quote, label_print and label_void operations. Labels produced by the Generate Label webhook are a different mechanism with their own behaviour, and nothing here describes how those are voided.

Because the message is passed through, the same underlying problem can read differently from one carrier to the next. Common causes worth checking before contacting support:

What to checkTypical cause
Carrier connectionThe carrier is not connected to the account, or its credentials no longer authenticate.
Shipping method mappingThe order’s shipping method is not mapped to a service the carrier account offers.
Carrier billingThe carrier account has a billing hold or has hit a spending limit.
Shipping addressThe address is incomplete, undeliverable, or a field is longer than the carrier allows.
Package dataWeight or dimensions are missing, zero, or outside the carrier’s accepted range.
Carrier availabilityThe carrier is rate limiting the account or is temporarily down.

Note

A failed label request does not always mean nothing happened at the carrier. Before retrying label_print, query the order’s shipments to confirm no label was created, so you do not pay for two.

# a label_print that failed — confirm nothing was created before retrying
query { order(id: "T3JkZXI6MTIzNDU2") { data { shipments { edges { node { shipping_labels { tracking_number } } } } } } }

Operations gated by internal permissions

Appearing in the schema does not mean every token can run an operation. The labor and performance-dashboard operations call Warehouse Hero, a separate ShipHero service that re-authorizes the same user against its own warehousehero.manage.* permissions.

A developer user cannot run these at all. Developer users bypass the Public API’s own permission check and ShipHero’s internal one, but Warehouse Hero has no such bypass, and developer users hold no permissions for it to find. There is no scope, token or request change that gets past this.

The affected operations are pickers_performance_dashboard, packers_performance_dashboard, labor_workers, labor_jobs, labor_worker_activities, labor_activity_start, labor_activity_modify, labor_activity_remove and labor_activity_finish. They surface the rejection as Data is temporarily unavailable. Please try again later, which is why a persistent failure here is not an outage.

To use them, authenticate as a regular ShipHero user who holds the Warehouse Hero permission, rather than as a developer user. Which warehousehero.manage.* permission applies varies by operation and is not visible in the Public API schema, so ask in the ShipHero Community rather than inferring it. Workforce Hero also has to be active for the warehouse, which support@shiphero.com enables.

None of these accept customer_account_id. They always run against the account that owns the token, so a 3PL cannot target a client account with them.

Note

A downstream permission requirement can be added without any schema change, so this list is accurate as of this page’s last update rather than permanently complete. If an operation fails consistently with a permission or availability error and nothing on this page explains it, ask in the ShipHero Community with the request_id.

Reporting an error to support

Include the request_id from the error. It identifies your exact request and payload, which is usually enough to explain the failure without a reproduction.

Every query and mutation can also return request_id as a field, so you can capture it on successful operations that behaved unexpectedly:

{
  request_id
  complexity
  orders {
    request_id
    complexity
  }
}

Ask in the ShipHero Community with the request_id. See Error Handling & Support.