Appearance
Errors & retries
Handle failures according to the operation. A failed or timed-out write may already have committed; a lost response is not proof that nothing changed.
Read an SDK error
FunnelSdkHttpError, exported by @funnelsgrove/runtime, exposes:
| Field | Meaning |
|---|---|
statusCode | HTTP response status. |
message | Human-readable error. |
code | Operation-specific or shared error code, when provided. |
retryable | Explicit server boolean, or null when unknown. |
retryAfter | Raw Retry-After header, or null when unavailable. |
checkoutRequestId | Checkout diagnostic ID, when provided. |
Network failures remain ordinary fetch errors. An absent code or retry hint on an older API is unknown, not a parsing failure. An explicit retryable: false prevents automatic replay; null does not grant permission to retry a write.
Request budgets
Current runtime user and upload operations accept an optional AbortSignal. Caller cancellation rejects with AbortError (or the supplied abort reason); an exhausted budget rejects with TimeoutError.
| Operation | Client budget | Replay policy |
|---|---|---|
| User reads and mutations | 25 seconds total, including the response body and retry delay | Operation-specific; see below. |
| Photo preparation | 25 seconds | One attempt. |
| Direct photo upload | 60 seconds after preparation | One attempt. |
| Legacy base64 upload | 25 seconds | One attempt. |
| Runtime configuration | 25 seconds shared across coalesced readers | Up to three attempts within that budget. |
Cancellation and budget exhaustion are never automatically replayed. Payment operations have their own attempt and recovery rules; do not apply the user-read retry policy to checkout.
When the runtime retries
getCurrentUser and the compatibility getUserSubscriptionStatus method may replay once after a network failure or HTTP 408, 429, 500, 502, 503, or 504, unless the response says retryable: false.
bootstrapUser uses that policy only with a nonempty idempotencyKey. A replay reuses the original serialized request body and key. Other user mutations execute once.
For an eligible replay, Retry-After seconds or an HTTP date sets the delay. Without a usable header, the delay is 250 ms. If that delay exceeds the remaining budget, the runtime returns the failure rather than retrying early.
Common responses
| Status or code | Meaning and next step |
|---|---|
400, SDK_INVALID_REQUEST | Correct the request before retrying. |
401, SDK_UNAUTHORIZED | Check the project key or operation's identity proof. |
403, SDK_FORBIDDEN | The requested scope or origin is not permitted. |
404, SDK_NOT_FOUND | Check the resource and project scope. |
409, SDK_CONFLICT | Resolve the conflicting state; read the operation's contract. |
409, SDK_CONCURRENT_UPDATE | Retry the same logical request after Retry-After: 1. |
413, SDK_PAYLOAD_TOO_LARGE | Reduce the payload. |
415, SDK_UNSUPPORTED_MEDIA_TYPE | Use a supported content type. |
429 | Wait for Retry-After; only replay operations that permit it. |
503, SDK_DEPENDENCY_UNAVAILABLE | Follow retryable and the operation-specific rules below. |
Operation-specific codes take precedence over shared codes. Older responses may contain only error. Do not treat an unfamiliar code or a server failure as proof of an inactive subscription or an unsuccessful payment.
Identity operations
Bootstrap: a transient scope-read failure may be retried once by the server before any write begins. If it still fails, the response is 503, SDK_DEPENDENCY_UNAVAILABLE, retryable: true, with Retry-After: 1. Failures after writing are subject to the operation's existing idempotency rules.
User updates and email capture: dependency failures after entering the write path are non-retryable. Earlier identity, consent, or event writes may have committed. Cancellation does not roll them back.
Subscription verification: a transient read failure can return retryable 503 after a bounded internal retry. Preserve the error state; it does not mean that access is inactive.
App Login: a dependency failure before the registration workflow can return retryable 503. After entering the workflow, a classified dependency failure has retryable: false without Retry-After. Registration may already exist. A later explicit login reuses the original registration identity and timestamp.
Checkout diagnostics
Checkout creation can return 401 with code: "checkout_identity_owner_unavailable" and a server-generated UUID checkoutRequestId. The safe message is Checkout identity owner is unavailable.
Keep the ID for support correlation. It is a lookup hint, not an ownership proof or permission to retry a purchase. Never log identity/access tokens, payment credentials, or customer data alongside it.
The runtime preserves this ID in FunnelSdkHttpError; current shared payments forward it in checkout failure telemetry. Older clients can omit it. See Payments for recovery by operation.
Proxies and headers
- The runtime sends
X-FunnelsGrove-Runtime-Versionfor diagnostics. It is optional and never authorizes a request. Signed storage uploads omit it. - Runtime-config requests may send
Cache-Control: no-cache. If you proxy the API, preserve the required CORS request headers and exposeRetry-After. - Treat a missing
checkoutRequestId, error code or retry hint as unknown rather than as a success.