ShipStation label generation fails with 500 NullReferenceException (duplicate Stamps.com carrier) #27

Open
opened 2026-06-29 08:36:10 +00:00 by rob · 0 comments
Owner

Summary

Generating shipping labels via ShipStation fails for the Stamps.com carrier.
ShipStation's V1 POST /shipments/createlabel returns HTTP 500
System.NullReferenceException
for every request, while getrates with the
identical carrier/service/address/weight succeeds. No label is ever produced.

Reproduced on prod (primabrands-llc) and locally (cb).

Impact

  • Retail Pick & Pack print jobs produce no labels for affected shipments.
  • Originally this also crashed the progress page with Missing parameter "id"
    (Laminas Segment::buildPath) because the empty print job had a null id.

Reproduction

vendor/bin/laminas retail:create-print-job \
  --tenant=primabrands-llc --user-id=2 --print-method=thermal --shipment-ids=5328

Investigation — ruled out (it is NOT our request)

Variable Result
testLabel: true vs false (real label) both 500 NRE
dimensions (bogus 1x1x1cm, removed, or real inches) all 500 NRE
shipTo.phone null vs set both 500 NRE
carrier/service/package codes valid — appear in listservices/listpackages, and getrates succeeds
payload structure valid — getrates returns rates with identical data

getrates -> 200; createlabel -> 500 NRE, no matter what we send. The failure
is in ShipStation's label-purchase path, server-side
(SS.OpenApi...ShipmentsController._CreateLabel line 238).

Root cause (strong suspect)

The ShipStation account has two Stamps.com carriers, both with
code: stamps_com:

shippingProviderId accountNumber nickname primary
382984 dthurston-7585 "Chase" false
493397 primabrands true

Our DB (ShipStationCarrier.id = 1, code = stamps_com_382984,
ss_code = stamps_com) is bound to the non-primary account (382984). We send
the ambiguous carrierCode: stamps_com; rating tolerates the ambiguity but
createlabel cannot resolve which Stamps.com account to buy from and NREs.

Remediation

  1. Primary: remove the duplicate Stamps.com carrier in ShipStation (keep one),
    re-sync carriers so carrierCode: stamps_com is unambiguous.
  2. Alt: point our ShipStationCarrier row at the primary provider and send a
    disambiguated carrierCode (stamps_com_493397). Needs a probe to confirm V1
    accepts the suffixed code.
  3. Optional UX: guard in ShipStationGenerator::createLabel() to translate a
    5xx/NRE into a readable operator message instead of leaking the .NET stack trace.
  4. If a clean reconnect still NREs -> escalate to ShipStation support with the
    captured request + _CreateLabel line 238 stack trace (unhandled bug on their side).

Already fixed in this branch (jv-fix)

  • GeneratePickListTask: redirect uses the first persisted print job; empty-job
    case no longer passes a null id (fixes Missing parameter "id" crash).
  • GeneratePickListTask + BackgroundController + ShipmentsController: empty job
    redirects back to Pick & Pack with an "Unable to generate the label" flash.
  • ShipStationGenerator: log path /tmp/shipstation.log -> data/logs/shipstation.log
    (the /tmp path was unwritable on prod, which had hidden the request/response).
## Summary Generating shipping labels via ShipStation fails for the Stamps.com carrier. ShipStation's V1 `POST /shipments/createlabel` returns **HTTP 500 `System.NullReferenceException`** for every request, while `getrates` with the identical carrier/service/address/weight succeeds. No label is ever produced. Reproduced on prod (`primabrands-llc`) and locally (`cb`). ## Impact - Retail Pick & Pack print jobs produce no labels for affected shipments. - Originally this also crashed the progress page with `Missing parameter "id"` (Laminas `Segment::buildPath`) because the empty print job had a null id. ## Reproduction ``` vendor/bin/laminas retail:create-print-job \ --tenant=primabrands-llc --user-id=2 --print-method=thermal --shipment-ids=5328 ``` ## Investigation — ruled out (it is NOT our request) | Variable | Result | |---|---| | `testLabel: true` vs `false` (real label) | both 500 NRE | | `dimensions` (bogus 1x1x1cm, removed, or real inches) | all 500 NRE | | `shipTo.phone` null vs set | both 500 NRE | | carrier/service/package codes | valid — appear in `listservices`/`listpackages`, and `getrates` succeeds | | payload structure | valid — `getrates` returns rates with identical data | `getrates` -> 200; `createlabel` -> 500 NRE, no matter what we send. The failure is in ShipStation's label-purchase path, server-side (`SS.OpenApi...ShipmentsController._CreateLabel line 238`). ## Root cause (strong suspect) The ShipStation account has **two Stamps.com carriers**, both with `code: stamps_com`: | shippingProviderId | accountNumber | nickname | primary | |---|---|---|---| | 382984 | dthurston-7585 | "Chase" | **false** | | 493397 | primabrands | — | **true** | Our DB (`ShipStationCarrier.id = 1`, `code = stamps_com_382984`, `ss_code = stamps_com`) is bound to the **non-primary** account (382984). We send the ambiguous `carrierCode: stamps_com`; rating tolerates the ambiguity but `createlabel` cannot resolve which Stamps.com account to buy from and NREs. ## Remediation 1. **Primary:** remove the duplicate Stamps.com carrier in ShipStation (keep one), re-sync carriers so `carrierCode: stamps_com` is unambiguous. 2. **Alt:** point our `ShipStationCarrier` row at the primary provider and send a disambiguated `carrierCode` (`stamps_com_493397`). Needs a probe to confirm V1 accepts the suffixed code. 3. **Optional UX:** guard in `ShipStationGenerator::createLabel()` to translate a 5xx/NRE into a readable operator message instead of leaking the .NET stack trace. 4. If a clean reconnect still NREs -> escalate to ShipStation support with the captured request + `_CreateLabel line 238` stack trace (unhandled bug on their side). ## Already fixed in this branch (jv-fix) - `GeneratePickListTask`: redirect uses the first persisted print job; empty-job case no longer passes a null `id` (fixes `Missing parameter "id"` crash). - `GeneratePickListTask` + `BackgroundController` + `ShipmentsController`: empty job redirects back to Pick & Pack with an "Unable to generate the label" flash. - `ShipStationGenerator`: log path `/tmp/shipstation.log` -> `data/logs/shipstation.log` (the `/tmp` path was unwritable on prod, which had hidden the request/response).
rob added the bugfulfillmentprodshipstation labels 2026-06-29 08:36:10 +00:00
Sign in to join this conversation.