Summary
When a streaming upload fails because the packed piece/CAR is below the SP minimum size, the actionable InvalidUploadSizeError is buried under generic Network request failed / StorageContext store failed messages by the time it reaches SDK consumers (e.g. filecoin-pin CLI).
Users see an opaque network failure instead of the real size constraint.
Expected behavior
Consumers should receive (or be able to surface) the underlying InvalidUploadSizeError message, e.g.:
Invalid upload size.
Details: Size 125 bytes is below minimum allowed size of 127 bytes or exceeds maximum allowed size of ...
Actual behavior
filecoin-pin (and any SDK consumer displaying StoreError.message) shows something like:
Failed to store on primary provider 2 (https://calib2.ezpdpz.net)
Details: StorageContext store failed: Failed to store piece on service provider - Network request failed
The real cause is only visible by walking the nested cause chain (or with debug logging).
Error chain (observed)
InvalidUploadSizeError — thrown in @filoz/synapse-core streaming upload when bytesUploaded < MIN_UPLOAD_SIZE (127 bytes) (upload-streaming.ts TransformStream.flush)
NetworkError — Network request failed (stream error during PUT to /pdp/piece/uploads/{uuid})
Error via createError() — StorageContext store failed: Failed to store piece on service provider - Network request failed (storage/context.ts)
StoreError — Failed to store on primary provider … with Details: taken from immediate cause message, not the deepest SynapseError (storage/manager.ts)
StoreError / SynapseError currently populate details from the immediate cause.message, so the useful InvalidUploadSizeError.details never surfaces.
How to reproduce
Via filecoin-pin (easiest end-to-end)
From a filecoin-pin checkout with calibration credentials (PRIVATE_KEY, etc. in .env):
# A lone `date` line packs to a ~125-byte CAR — below the 127-byte minimum.
TMPFILE=$(mktemp) && date > "$TMPFILE" && \
LOG_LEVEL=debug NODE_DEBUG=fetch \
npx tsx src/cli.ts add "$TMPFILE" --network calibration --provider-id 2
Observe:
- User-facing failure:
Network request failed
NODE_DEBUG=fetch shows HTTP 201 on POST …/pdp/piece/uploads, then failed PUT …/pdp/piece/uploads/{uuid} with Invalid upload size
- Debug JSON log includes nested cause:
InvalidUploadSizeError with full size details
Provider 2 (calib2.ezpdpz.net) ping succeeds; this is not connectivity.
Minimal SDK-level repro (conceptual)
Any synapse.storage.upload() / StorageContext.store() call with streaming body whose final size is < SIZE_CONSTANTS.MIN_UPLOAD_SIZE (127) against a provider enforcing that minimum should reproduce the wrapped error chain.
Suggested fixes (synapse layer)
Any of these (or a combination) would address the root cause:
- synapse-core: Do not wrap
InvalidUploadSizeError in NetworkError when the upload stream fails on size validation; preserve the original error (or set it as cause on a typed error that retains details).
- synapse-sdk
createError(): When wrapping, walk the cause chain for the deepest SynapseError and prefer its details / shortMessage in the outer message.
StoreError construction (storage/manager.ts): When primary store() fails, attach root-cause details from the nested chain (similar to how SynapseError could be enhanced globally).
Fallback if this is rejected at the synapse layer
If maintainers prefer not to change synapse-sdk/synapse-core error propagation, the work should still be tracked in filecoin-pin (either move this issue there or open a sibling issue) to:
- Walk the
cause chain in CLI upload paths (add, import) — same pattern as existing describeLockupShortfall() for InsufficientLockupFunds
- Surface known errors like
InvalidUploadSizeError with actionable hints (e.g. pad test payloads past CAR overhead; see filecoin-pin DEVELOPMENT.md)
That would be a consumer-side workaround, not a root fix for other SDK users.
Context
Discovered while testing calibration uploads with filecoin-pin’s date > $TMPFILE one-liner. CAR overhead can push a tiny unique payload just under the SP minimum; the misleading network error made this hard to diagnose without LOG_LEVEL=debug + NODE_DEBUG=fetch.
Summary
When a streaming upload fails because the packed piece/CAR is below the SP minimum size, the actionable
InvalidUploadSizeErroris buried under genericNetwork request failed/StorageContext store failedmessages by the time it reaches SDK consumers (e.g. filecoin-pin CLI).Users see an opaque network failure instead of the real size constraint.
Expected behavior
Consumers should receive (or be able to surface) the underlying
InvalidUploadSizeErrormessage, e.g.:Actual behavior
filecoin-pin (and any SDK consumer displaying
StoreError.message) shows something like:The real cause is only visible by walking the nested
causechain (or with debug logging).Error chain (observed)
InvalidUploadSizeError— thrown in@filoz/synapse-corestreaming upload whenbytesUploaded < MIN_UPLOAD_SIZE(127 bytes) (upload-streaming.tsTransformStream.flush)NetworkError—Network request failed(stream error during PUT to/pdp/piece/uploads/{uuid})ErrorviacreateError()—StorageContext store failed: Failed to store piece on service provider - Network request failed(storage/context.ts)StoreError—Failed to store on primary provider …withDetails:taken from immediate cause message, not the deepestSynapseError(storage/manager.ts)StoreError/SynapseErrorcurrently populatedetailsfrom the immediatecause.message, so the usefulInvalidUploadSizeError.detailsnever surfaces.How to reproduce
Via filecoin-pin (easiest end-to-end)
From a filecoin-pin checkout with calibration credentials (
PRIVATE_KEY, etc. in.env):Observe:
Network request failedNODE_DEBUG=fetchshows HTTP 201 onPOST …/pdp/piece/uploads, then failedPUT …/pdp/piece/uploads/{uuid}withInvalid upload sizeInvalidUploadSizeErrorwith full size detailsProvider 2 (
calib2.ezpdpz.net) ping succeeds; this is not connectivity.Minimal SDK-level repro (conceptual)
Any
synapse.storage.upload()/StorageContext.store()call with streaming body whose final size is< SIZE_CONSTANTS.MIN_UPLOAD_SIZE(127) against a provider enforcing that minimum should reproduce the wrapped error chain.Suggested fixes (synapse layer)
Any of these (or a combination) would address the root cause:
InvalidUploadSizeErrorinNetworkErrorwhen the upload stream fails on size validation; preserve the original error (or set it ascauseon a typed error that retainsdetails).createError(): When wrapping, walk thecausechain for the deepestSynapseErrorand prefer itsdetails/shortMessagein the outer message.StoreErrorconstruction (storage/manager.ts): When primarystore()fails, attach root-causedetailsfrom the nested chain (similar to howSynapseErrorcould be enhanced globally).Fallback if this is rejected at the synapse layer
If maintainers prefer not to change synapse-sdk/synapse-core error propagation, the work should still be tracked in filecoin-pin (either move this issue there or open a sibling issue) to:
causechain in CLI upload paths (add,import) — same pattern as existingdescribeLockupShortfall()forInsufficientLockupFundsInvalidUploadSizeErrorwith actionable hints (e.g. pad test payloads past CAR overhead; see filecoin-pinDEVELOPMENT.md)That would be a consumer-side workaround, not a root fix for other SDK users.
Context
Discovered while testing calibration uploads with filecoin-pin’s
date > $TMPFILEone-liner. CAR overhead can push a tiny unique payload just under the SP minimum; the misleading network error made this hard to diagnose withoutLOG_LEVEL=debug+NODE_DEBUG=fetch.