can1357/oh-my-pi · error · ConfigurationError

Both CLAUDE_CODE_CLIENT_CERT and CLAUDE_CODE_CLIENT_KEY must

Error message

Both CLAUDE_CODE_CLIENT_CERT and CLAUDE_CODE_CLIENT_KEY must be set for mTLS.

What it means

This ConfigurationError is thrown when configuring mTLS client authentication for an Anthropic-compatible endpoint. mTLS requires BOTH a client certificate and its corresponding private key; providing only one is always a configuration mistake because the TLS handshake cannot complete. The library detects the partial configuration early and fails with a clear message instead of an opaque TLS error later.

Source

Thrown at packages/ai/src/providers/anthropic.ts:1321

		}
	}

	return inline;
}

function resolveFoundryTlsOptions(model: Model<"anthropic-messages">): FoundryTlsOptions | undefined {
	if (model.provider !== "anthropic") return undefined;
	if (!isFoundryEnabled()) return undefined;

	const cacheKey = foundryTlsOptionsCacheKey();
	if (foundryTlsOptionsCache.has(cacheKey)) return foundryTlsOptionsCache.get(cacheKey);

	const ca = resolvePemValue($env.NODE_EXTRA_CA_CERTS, "NODE_EXTRA_CA_CERTS");
	const cert = resolvePemValue($env.CLAUDE_CODE_CLIENT_CERT, "CLAUDE_CODE_CLIENT_CERT");
	const key = resolvePemValue($env.CLAUDE_CODE_CLIENT_KEY, "CLAUDE_CODE_CLIENT_KEY");

	if ((cert && !key) || (!cert && key)) {
		throw new AIError.ConfigurationError(
			"Both CLAUDE_CODE_CLIENT_CERT and CLAUDE_CODE_CLIENT_KEY must be set for mTLS.",
		);
	}

	const options: FoundryTlsOptions = {};
	if (ca) options.ca = [...tls.rootCertificates, ca];
	if (cert) options.cert = cert;
	if (key) options.key = key;
	const resolved = Object.keys(options).length > 0 ? options : undefined;
	foundryTlsOptionsCache.set(cacheKey, resolved);
	return resolved;
}

function buildCoworkTlsFetchOptions(
	model: Model<"anthropic-messages">,
	baseUrl: string | undefined,
): AnthropicFetchOptions | undefined {
	if (model.provider !== "anthropic") return undefined;

View on GitHub (pinned to 9690622007)

Solutions

  1. Set both CLAUDE_CODE_CLIENT_CERT and CLAUDE_CODE_CLIENT_KEY (PEM contents) in the environment before starting the process.
  2. If mTLS is not required, clear BOTH variables rather than just one.
  3. Check for typos in the variable names and verify resolvePemValue input (file path vs inline PEM) resolves to a non-empty value for both.
  4. Verify the key matches the certificate (same keypair); mismatched pairs fail even when both are set.

Example fix

// before
export CLAUDE_CODE_CLIENT_CERT=$(cat client.pem)
# CLAUDE_CODE_CLIENT_KEY not set

// after
export CLAUDE_CODE_CLIENT_CERT=$(cat client.pem)
export CLAUDE_CODE_CLIENT_KEY=$(cat client-key.pem)
Defensive patterns

Strategy: validation

Validate before calling

const cert = process.env.CLAUDE_CODE_CLIENT_CERT;
const key = process.env.CLAUDE_CODE_CLIENT_KEY;
const mTLSRequested = Boolean(cert) !== Boolean(key);
if (mTLSRequested) {
  throw new Error("Set BOTH CLAUDE_CODE_CLIENT_CERT and CLAUDE_CODE_CLIENT_KEY (or neither).");
}

Type guard

function hasCompleteMtlsConfig(env: NodeJS.ProcessEnv): env is NodeJS.ProcessEnv & { CLAUDE_CODE_CLIENT_CERT: string; CLAUDE_CODE_CLIENT_KEY: string } {
  return (
    (typeof env.CLAUDE_CODE_CLIENT_CERT === "string" && env.CLAUDE_CODE_CLIENT_CERT.length > 0) ===
    (typeof env.CLAUDE_CODE_CLIENT_KEY === "string" && env.CLAUDE_CODE_CLIENT_KEY.length > 0)
  );
}

Prevention

When it happens

Trigger: Calling an Anthropic provider constructor/factory that builds a Foundry-style TLS config where exactly one of CLAUDE_CODE_CLIENT_CERT or CLAUDE_CODE_CLIENT_KEY is set in the environment (the other is unset or empty after resolvePemValue processing).

Common situations: Partial secrets setup in CI/CD (one secret added, the other forgotten); copying cert file content but forgetting the key (or vice versa) when proxying through a corporate mTLS gateway; rotating credentials and updating only one env var; typo in one of the two variable names.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


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