can1357/oh-my-pi · error · ProviderResponseError
An unknown error occurred
Error message
An unknown error occurred
What it means
AIError.ProviderResponseError is thrown in the Anthropic streaming provider when a completed stream ends with output.stopReason of 'aborted' or 'error'. The provider ran the request but the upstream returned a terminal error state instead of normal completion; output.errorMessage is used when the API supplied one, otherwise the generic 'An unknown error occurred' fallback is used. It carries kind:'output' and the provider name so callers can distinguish provider-side failures from request-configuration failures.
Source
Thrown at packages/ai/src/providers/anthropic.ts:2695
// A stop_reason arrived via message_delta, so generation finished;
// only the trailing message_stop frame is missing (non-conforming
// gateway). Degrade to best-effort instead of discarding the turn.
reportAnthropicEnvelopeAnomaly("stream ended before message_stop");
}
if (openBlocks.size > 0) {
for (const [openIndex, openBlock] of openBlocks) {
reportAnthropicEnvelopeAnomaly(
`stream ended with an unterminated ${openBlock.kind} block at index ${openIndex}`,
);
if (openBlock.kind === "ignored" || openBlock.contentIndex < 0) continue;
const danglingBlock = blocks[openBlock.contentIndex];
if (danglingBlock) finalizeStreamBlock(danglingBlock, openBlock.contentIndex);
}
openBlocks.clear();
}
if (output.stopReason === "aborted" || output.stopReason === "error") {
throw new AIError.ProviderResponseError(output.errorMessage ?? "An unknown error occurred", {
provider: model.provider,
kind: "output",
});
}
break;
} catch (streamError) {
const streamFailure = activeAbortTracker.getLocalAbortReason() ?? streamError;
if (
!disableStrictTools &&
firstTokenTime === undefined &&
hasStrictAnthropicTools(params) &&
AIError.isGrammarError(streamFailure)
) {
// Log-only: the retried turn must not carry an errorMessage on
// success (consumers treat its presence as failure).
logger.warn("anthropic: strict tools rejected, retrying without strict tools", {
model: model.id,
error: await finalizeErrorMessage(streamFailure, rawRequestDump),View on GitHub (pinned to 9690622007)
Solutions
- Inspect the full output object / provider logs around the throw to find the real upstream stopReason and any error event payload, since 'An unknown error occurred' hides the cause
- Retry the request with backoff if stopReason was 'error' due to overloaded_error or a transient Anthropic incident (check status.anthropic.com)
- Catch AIError.ProviderResponseError, check the kind==='output' and provider fields, and surface a retryable failure to your app instead of crashing
- If aborted unexpectedly, check for request cancellation (AbortSignal) or gateway/proxy timeouts in your infrastructure
- Update @oh-my-pi/pi-ai in case the missing errorMessage parsing has been fixed in a newer version
Example fix
// before: raw crash on transient overload
const result = await session.prompt(model, messages);
// after: catch provider output errors and retry
try {
const result = await session.prompt(model, messages);
} catch (err) {
if (err instanceof AIError.ProviderResponseError && err.kind === "output") {
// log err.provider + stopReason, retry with backoff
}
throw err;
} Defensive patterns
Strategy: try-catch
Type guard
function isProviderOutputError(err: unknown): err is AIError.ProviderResponseError {
return err instanceof AIError.ProviderResponseError && err.kind === "output";
} Try / catch
try {
const result = await session.prompt(model, messages);
} catch (err) {
if (err instanceof AIError.ProviderResponseError && err.kind === "output") {
logger.warn("anthropic stream ended in error state", { provider: err.provider });
// retry with backoff or surface a user-facing failure
return;
}
throw err;
} Prevention
- Always wrap streaming provider calls in try/catch for AIError.ProviderResponseError
- Implement automatic retry with exponential backoff for kind==='output' errors, which are frequently transient (overloaded_error)
- Monitor Anthropic status and set a lower concurrency during incidents
- Check AbortSignal handling so intentional cancellations are not mistaken for provider errors
- Keep the SDK updated so upstream error payloads are parsed into output.errorMessage
When it happens
Trigger: Calling an Anthropic model (streaming path in packages/ai/src/providers/anthropic.ts) when the stream finishes with stopReason 'aborted' or 'error' and no errorMessage text was captured — e.g. the upstream aborted mid-generation, an overloaded_error or other API error event terminated the stream without a parsed message, or an internal error event carried no payload.
Common situations: Anthropic API returns overloaded_error (529) or a transient internal error mid-stream; a proxy/gateway truncates the stream; the request was cancelled upstream and surfaced as 'aborted' with no message; SDK version where the error event's message field isn't parsed into output.errorMessage, so only the fallback string appears.
Related errors
- Attempted to iterate over an Anthropic response with no body
- Anthropic SDK request did not expose a stream response
- received ${event.type} before message_start
- AbortError
- stream ended before message_stop
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/a342803687ebd7d4.
Report an issue: GitHub.