can1357/oh-my-pi · error · ProviderResponseError

anthropic-messages: upstream ${message.stopReason}

Error message

anthropic-messages: upstream ${message.stopReason}

What it means

encodeResponse converts an internal AssistantMessage back into an Anthropic Messages API response. If the upstream run ended with stopReason 'error' or 'aborted', it throws ProviderResponseError using the recorded errorMessage, or the fallback 'anthropic-messages: upstream error/aborted'. The server therefore surfaces upstream provider failures to the API client as an error response rather than silently returning an empty/malformed completion.

Source

Thrown at packages/ai/src/providers/anthropic-messages-server.ts:500

				blocks.push({ type: "tool_use", id: c.id, name: c.name, input: c.arguments ?? {} });
				break;
		}
	}
	return blocks;
}

function encodeUsage(message: AssistantMessage): Record<string, unknown> {
	return {
		input_tokens: message.usage.input,
		output_tokens: message.usage.output,
		cache_read_input_tokens: message.usage.cacheRead,
		cache_creation_input_tokens: message.usage.cacheWrite,
	};
}

export function encodeResponse(message: AssistantMessage, requestedModelId: string): Record<string, unknown> {
	if (message.stopReason === "error" || message.stopReason === "aborted") {
		throw new AIError.ProviderResponseError(
			message.errorMessage ?? `anthropic-messages: upstream ${message.stopReason}`,
			{
				provider: "anthropic",
				kind: "output",
			},
		);
	}
	return {
		id: message.responseId ?? newMessageId(),
		type: "message",
		role: "assistant",
		model: requestedModelId,
		content: encodeContentBlocks(message),
		stop_reason: mapStopReasonOut(message.stopReason),
		// TODO: surface the matched stop sequence once pi-ai's
		// `AssistantMessage.stopReason` carries the matched string. Intentionally
		// `null` for now (Anthropic schema allows it).
		stop_sequence: null,

View on GitHub (pinned to 9690622007)

Solutions

  1. Inspect message.errorMessage (preferred) over the fallback text — it carries the actual upstream failure reason
  2. Check upstream provider credentials and quota (the usual cause of stopReason 'error')
  3. Treat 'upstream aborted' as client cancellation: verify the caller isn't closing the connection prematurely
  4. Retry transient upstream errors with backoff at the client of this server
  5. Add health checks/alerting on the upstream provider if these errors appear in clusters

Example fix

// before: no handling of upstream failure responses
const res = await serverClient.post("/v1/messages", body);
return res.json();
// after
const res = await serverClient.post("/v1/messages", body);
if (!res.ok) {
  const err = await res.json();
  if (isTransientUpstream(err)) return retryWithBackoff(() => post(body));
  throw new Error(err.error?.message ?? "upstream provider failure");
}
Defensive patterns

Strategy: try-catch

Type guard

import { AIError } from "@oh-my-pi/pi-ai";
function isUpstreamFailure(e: unknown): e is AIError.ProviderResponseError {
  return e instanceof AIError.ProviderResponseError;
}

Try / catch

try {
  return encodeResponse(message, modelId);
} catch (err) {
  if (isUpstreamFailure(err)) {
    // stopReason was error/aborted upstream; prefer err's contextual info and the
    // original message.errorMessage over the generic fallback text
    if (message.stopReason === "aborted") return clientCancelledResponse();
    return upstreamBadGatewayResponse(message.errorMessage ?? err.message);
  }
  throw err;
}

Prevention

When it happens

Trigger: A request proxied through the anthropic-messages server whose downstream provider call produced an AssistantMessage with stopReason 'error' or 'aborted' — provider outage, upstream auth failure, or the original caller aborting mid-stream.

Common situations: The downstream provider API key expired or is invalid; upstream provider is rate-limiting or down; the end client disconnected and the run was aborted; streaming interrupted between server and upstream provider.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/e4d256f0f750a4c7. Report an issue: GitHub.