can1357/oh-my-pi · error · AnthropicConnectionError

AnthropicConnectionError

Error message

AnthropicConnectionError

What it means

AnthropicClient.#send retries fetch-level failures (network errors) up to maxRetries with backoff. If the final attempt still throws a low-level network error (and it is not already a connection-timeout error), it is wrapped in AnthropicConnectionError. This means the client could not establish or complete the HTTP connection to the Anthropic API at all — DNS failure, connection refused/reset, TLS error, or socket hang-up.

Source

Thrown at packages/ai/src/providers/anthropic-client.ts:245

		const maxRetryDelayMs = options?.maxRetryDelayMs ?? opts.maxRetryDelayMs ?? 60_000;
		const url = `${opts.baseURL ?? "https://api.anthropic.com"}${path}`;
		const headers = this.#buildHeaders(options?.headers);
		const body = JSON.stringify(params);

		for (let attempt = 0; ; attempt++) {
			if (callerSignal?.aborted) throw createAbortError();

			let response: Response;
			try {
				response = await this.#fetchOnce(fetchFn, url, headers, body, timeoutMs, callerSignal);
			} catch (error) {
				if (callerSignal?.aborted) throw createAbortError();
				if (attempt < maxRetries) {
					await this.#backoff(attempt, undefined, callerSignal);
					continue;
				}
				if (error instanceof AIError.AnthropicConnectionTimeoutError) throw error;
				throw new AIError.AnthropicConnectionError(error);
			}

			if (response.ok) return response;

			if (attempt < maxRetries && shouldRetryResponse(response)) {
				// Bound the server-directed wait: an over-cap `retry-after` declines
				// the retry and surfaces the original error (status/body/headers
				// intact) so higher-level recovery can run. A non-positive cap disables enforcement.
				// Checked before draining the body so `fromResponse` can still read it.
				const headerDelayMs = retryDelayFromHeaders(response.headers);
				if (headerDelayMs !== undefined && maxRetryDelayMs > 0 && headerDelayMs > maxRetryDelayMs) {
					throw await AIError.AnthropicApiError.fromResponse(response, callerSignal);
				}
				await response.body?.cancel().catch(() => {});
				await this.#backoff(attempt, response.headers, callerSignal);
				continue;
			}

View on GitHub (pinned to 9690622007)

Solutions

  1. Check basic connectivity to the API endpoint (curl -v https://api.anthropic.com) and fix network/VPN/proxy issues
  2. Verify ANTHROPIC_BASE_URL (or configured baseURL) — wrong host, port, or scheme produces exactly this wrapped failure
  3. Inspect the wrapped `cause` of AnthropicConnectionError for the underlying errno (ENOTFOUND, ECONNREFUSED, etc.) to target the fix
  4. Increase maxRetries if failures are transient (flaky networks), and add application-level backoff for long outages
  5. If behind a corporate proxy, configure HTTPS_PROXY and the agent options the client accepts

Example fix

// before: no diagnostics, fails in prod VPC
const client = new AnthropicClient(apiKey);
// after: explicit endpoint + cause logging
try {
  await client.request(...);
} catch (e) {
  if (e instanceof AIError.AnthropicConnectionError) {
    logger.error("anthropic unreachable", { cause: e.cause?.message, baseURL });
  }
  throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight endpoint sanity check
const url = new URL(baseURL ?? "https://api.anthropic.com");
if (url.protocol !== "https:" && process.env.NODE_ENV === "production") {
  throw new Error(`Anthropic baseURL must be https in production: ${url}`);
}

Type guard

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

Try / catch

try {
  return await client.request(req);
} catch (e) {
  if (isAnthropicConnectionError(e)) {
    // network-level failure after retries; log cause (ENOTFOUND/ECONNREFUSED/TLS) and back off
    logger.error("anthropic unreachable", { cause: (e.cause as Error)?.message });
    await Bun.sleep(retryDelay);
    return client.request(req); // or surface a user-facing outage error
  }
  throw e;
}

Prevention

When it happens

Trigger: fetch() throwing on any attempt of a request() call and retries exhausted: DNS resolution failure, ECONNREFUSED/ECONNRESET, TLS handshake failure, proxy misconfiguration, or the machine being offline.

Common situations: Corporate proxies/firewalls blocking api.anthropic.com; ANTHROPIC_BASE_URL pointing at a wrong port or a down local gateway; VPN required but not connected; IPv6/DNS issues in containers; transient network blips exceeding the retry budget.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


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