JuliusBrussee/caveman · error

request auth unavailable

Error message

request auth unavailable

What it means

Thrown in the provider's apply() when the model registry's getApiKeyAndHeaders(model) resolver fails to produce auth credentials (returns ok:false) for a routed model. The library requires a valid auth resolver result before it will attach Pi-configured provider/auth headers to outgoing requests; without them the request would be unauthenticated, so it aborts. This is a runtime configuration/credential availability failure, not a network error.

Solutions

  1. Run the provider's auth/login flow (OAuth) or configure an API key for the routed model so getApiKeyAndHeaders succeeds.
  2. Verify the model ID resolves to a registered provider in ctx.modelRegistry (check provider config/JSON).
  3. Check that credentials were not deleted or expired; re-authenticate.
  4. If the route is only needed conditionally, remove or fix the route so the !route branch is taken, or set the compatibility flag if applicable.

Example fix

// before: routed model with no credentials registered
modelRegistry.register({ id: "my-model", route: "anthropic/claude" });

// after: register the model together with its API key/auth headers
modelRegistry.register({
  id: "my-model",
  route: "anthropic/claude",
  apiKey: process.env.ANTHROPIC_API_KEY,
  authHeaders: { "x-api-key": process.env.ANTHROPIC_API_KEY },
});
Defensive patterns

Strategy: try-catch

Validate before calling

// before opening the gate, confirm the model resolves with credentials
const probe = await ctx.modelRegistry.getApiKeyAndHeaders(model);
if (!probe.ok) throw new Error(`no credentials resolvable for model ${model.id}; configure an API key or run auth login`);

Type guard

function hasResolvedAuth(a: { ok: boolean; headers?: Record<string, string> }): a is { ok: true; headers: Record<string, string> } {
  return a.ok === true && typeof a.headers === "object";
}

Try / catch

try {
  await provider.apply(...);
} catch (e) {
  if (e instanceof Error && e.message === "request auth unavailable") {
    // prompt/trigger re-auth (OAuth login) or configure the provider API key
  } else throw e;
}

Prevention

When it happens

Trigger: apply() is invoked from openGate for a model that (a) has no OAuth token available, (b) has a route configured, (c) does not have a compatibility issue flag set, and (d) whose ctx.modelRegistry.getApiKeyAndHeaders(model) call returns { ok: false } — i.e. no API key/headers could be resolved for that model.

Common situations: Provider configured with a route but no API key or OAuth login for the model; credentials removed or expired from the model registry; model ID mistyped so no registered provider matches it; running in an environment where the auth store is unavailable (CI without prior login).

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/c808d10869181d5b. Report an issue: GitHub.

Appendix: source

Thrown at packages/pi-extension/src/provider.ts:95

    // recover the effective cache option or distinguish another extension's
    // removal, so leave this specific endpoint direct. Responses is unaffected.
    const compatibilityIssue = model.api === "openai-completions" && original.includes("api.openai.com")
      ? "Pi cannot preserve this OpenAI Chat endpoint's prompt cache keys through routing"
      : undefined;
    let headerIssue: string | undefined;
    let oauth = true;
    try {
      oauth = ctx.modelRegistry.isUsingOAuth(model);
    } catch {
      // Cannot determine the auth kind ⇒ treat as OAuth and refuse (uncertain ⇒ direct).
    }
    if (!oauth && route && !compatibilityIssue) {
      try {
        // Pi adds configured provider/auth headers during request preparation;
        // they need not appear on model.headers. Use its public resolver and
        // discard the API key. Never log values or replace the auth handler.
        const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
        if (!auth.ok) throw new Error("request auth unavailable");
        const { headers } = auth;
        const published = {
          ...(this.compatUpstreams ? { compat_upstreams: this.compatUpstreams } : {}),
          ...(this.compatForwardHeaders ? { compat_forward_headers: this.compatForwardHeaders } : {}),
        };
        const missingHeaders = [...new Set([
          ...unforwardedProviderHeaders(model.api, model.provider, model.headers, published),
          ...unforwardedProviderHeaders(model.api, model.provider, headers, published),
        ])].sort();
        if (missingHeaders.length) {
          const remedy = missingHeaders.some(name => ["authorization", "x-api-key", "x-goog-api-key"].includes(name.toLowerCase()))
            ? "the proxy cannot preserve this authentication override; keep the provider direct"
            : `configure compat.${model.provider}.forward_headers`;
          headerIssue = `provider headers ${missingHeaders.join(", ")} are not forwarded; ${remedy}`;
        }
        if (!headerIssue) {
          let sessionId: string | undefined;
          try { sessionId = ctx.sessionManager.getSessionId(); } catch { /* unknown session affinity stays direct */ }

View on GitHub (pinned to 3ee70a1026)