Transient Retry
client.commit.apply(...) and OperationBuilder.commit(...) assign one UUID
submissionId to the logical submission and derive a stable eventRequestId
for each zero-based chunk ordinal. A timeout, dropped connection, or server 5xx
retries the same request identity. If the original request landed, the backend
returns its immutable receipt instead of applying it again.
Auto-retry applies whenever retry is enabled, the attempt budget remains, and
the failure is a transient stream failure — regardless of chunk ordinal,
operation count, operation kind, or whether streamId was supplied by the
caller.
A successful call returns an OperationSubmitResult — the aggregate write
result with the ordered exact receipts nested under receipts, one per chunk.
Advanced Stream Identity
Section titled “Advanced Stream Identity”Pass submissionId when your application needs to record the logical identity
before calling the SDK. Otherwise the SDK mints it before the first request.
streamId remains observation-only and is preserved across retries; it is not
the idempotency key.
Tuning Retry
Section titled “Tuning Retry”Pass retry: false to disable automatic retry:
await client.commit.apply(org, repo, message, operations, { retry: false })Pass a RetryPolicyOptions value to override defaults:
await client.commit.apply(org, repo, message, operations, { retry: { maxAttempts: 4, baseDelayMs: 100, maxDelayMs: 1000, },})OperationBuilder.commit(...) accepts the same retry option.
Partial Submissions
Section titled “Partial Submissions”The SDK throws PartialStreamSubmissionError when a chunk remains ambiguous after retry, or when a later chunk is definitely rejected after earlier receipts were acknowledged. The error carries completedReceipts, submissionId, chunkOrdinal, eventRequestId, and pendingOutcome.
acknowledgedOperationCount preserves the size of the acknowledged prefix, and lastAcknowledgedRepoSeq preserves its latest event sequence when one exists. These are progress diagnostics, not evidence about the pending append. attemptedAppendOutcome is the compatibility discriminator: "not_applied" maps to pendingOutcome: "absent"; "unknown" maps to pendingOutcome: "unknown".
For pendingOutcome: "unknown", recover with:
const receipt = await client.commit.getReceipt(org, repo, error.eventRequestId)Opaque NOT_FOUND means no visible receipt exists; only then may the identical chunk be retried with the same identity. A returned receipt is the outcome. For pendingOutcome: "absent", the pending request was definitely rejected, while completedReceipts still records earlier committed chunks.
After OperationBuilder.commit(...) throws PartialStreamSubmissionError, that builder is sealed: later mutation or commit calls fail before transport. After any outcome-unknown append has stopped, reconcile from a later verified checkpoint and create a new plan and builder.
Definite first-request rejections propagate as WarmHubError with the original kind and code. If every accepted physical operation is rejected, the high-level call throws AllStreamOperationsFailedError; its receipts field contains the exact no_event receipts and its operations field is their flattened operation sequence. Mixed successful and failed envelopes return normally for caller inspection. A low-level client.stream.append(...) returns a StreamAppendResult object; the receipt is nested under its receipt property, alongside per-operation results and optional top-level metadata — including all-failed and no-op outcomes.
AllStreamOperationsFailedError contract
Section titled “AllStreamOperationsFailedError contract”In TypeScript, detect this error with isAllStreamOperationsFailedError(error), not error instanceof AllStreamOperationsFailedError. The helper also recognizes the error after toWarmHubError normalization and when a different copy of the SDK threw it, both of which instanceof misses. Only the original error carries receipts, operations and result; a normalized error keeps its code and message.
AllStreamOperationsFailedError carries a code field. When every per-operation failure shares the same error code, code is set to that shared code. When failures are mixed, code is set to the string sentinel STREAM_ALL_OPERATIONS_FAILED.
Importantly, isRetryable (TypeScript SDK) and is_retryable (Python SDK) always return false for AllStreamOperationsFailedError, even when code is RATE_LIMITED. The stream request completed, and the server refused each operation on its own; per-operation refusals are not transient stream failures, so the SDK spends none of its retry budget on them and never retries them automatically. If your error-handling logic applies generic RATE_LIMITED retry handling by branching on error.code, add an explicit guard for this error type:
import { isAllStreamOperationsFailedError } from '@warmhub/sdk-ts'
if (isAllStreamOperationsFailedError(error)) { // isRetryable(error) is false here even if error.code === 'RATE_LIMITED'. // Correct the rejected inputs, then resubmit. On the original error, // error.receipts and error.operations show which inputs were rejected. return}Gate retry loops with isRetryable(error): it returns false for this error, so the loop stops. Inspect receipts and operations to understand which inputs were rejected before correcting and resubmitting.
Practical Rule
Section titled “Practical Rule”When you catch PartialStreamSubmissionError, preserve completedReceipts, acknowledgedOperationCount, and lastAcknowledgedRepoSeq, then use eventRequestId for receipt lookup when the outcome is unknown. Never mint a replacement identity for an ambiguous retry. When isAllStreamOperationsFailedError(error) is true, check error.code to identify the shared failure reason (or STREAM_ALL_OPERATIONS_FAILED for mixed failures), then inspect the original error’s exact receipts and flattened operations before correcting the rejected inputs. Do not apply generic RATE_LIMITED retry logic to this error — gate retries with isRetryable(error) (is_retryable in Python), which is always false for it regardless of code.