Skip to content

Transient Retry

client.commit.apply(...) and OperationBuilder.commit(...) automatically retry some ambiguous first-chunk failures: timeouts, dropped connections, and server 5xx responses on the very first stream append, where no operation has been acknowledged yet.

In plain terms: simple single-op writes retry automatically; multi-op writes and writes using advanced stream options surface ambiguous failures as PartialStreamSubmissionError. If the backend deterministically rejects every submitted operation, the SDK throws AllStreamOperationsFailedError with the per-op failure ledger on error.result and error.operations.

Auto-retry is conservative — it only fires when every condition holds:

  • The first chunk contains exactly one operation (multi-op chunks are rejected because the backend applies each op in its own transaction; a mid-chunk failure may have committed an arbitrary prefix).
  • No operation in the chunk references $N / #N allocation tokens (a fresh streamId on retry would resolve those tokens to a different identity than the first attempt may have landed).
  • No streamId or non-empty allocatedTokens was supplied (manual-resume mode bypasses retry).

When auto-retry fires, the SDK mints a fresh streamId and re-issues the operation. The retry does not prove the original attempt did not land — definite rejections on the retry are wrapped as PartialStreamSubmissionError so the “inspect repo state before continuing” guarantee survives. Callers needing safe retry should make add operations idempotent with skipExisting: true — pass it via client.commit.apply’s options or set it on each OperationBuilder.add(...) op directly.

For callers, a successful retry behaves like a single successful submission: the method resolves with the server’s operation results.

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.

The SDK throws PartialStreamSubmissionError whenever an ambiguous or partial append outcome cannot be reduced to a clean success or definite failure. This covers both:

  • Mid-stream failures. A multi-chunk submission whose first chunks acknowledged but a later chunk failed ambiguously. completedOperations lists the operations the SDK knows landed.
  • First-chunk ineligible-for-retry failures. The first append fails ambiguously, but auto-retry is disabled (retry: false) or ineligible (multi-op first chunk, token-using ops, or manual-resume mode). completedOperations is empty, but the failed append may still have landed server-side.

In every case, the failed append may have landed server-side, so inspect repository state before submitting additional operations.

Definite request-level rejections propagate as WarmHubError with the original kind and code. Examples include UNAUTHENTICATED, FORBIDDEN, VALIDATION_ERROR, RATE_LIMITED, and UNRESOLVED_TOKEN. Definite operation-level rejections can also resolve as mixed partial results; when every operation is rejected, they throw AllStreamOperationsFailedError so callers can still inspect each failed op.

Retry automatically when the SDK returns success or when a normal retryable WarmHubError is safe for your workload. When you catch PartialStreamSubmissionError, treat repository state as the source of truth before deciding what to submit next. When you catch AllStreamOperationsFailedError, use error.operations to fix the rejected inputs before retrying.