can1357/oh-my-pi · error · ProviderHttpError

mnemopi remote LLM request unauthorized (401)

Error message

mnemopi remote LLM request unauthorized (401)

What it means

callRemoteLlm sends a request to the configured remote LLM endpoint and maps an HTTP 401 response to this ProviderHttpError (status 401, carrying the response headers). It means the configured credentials were rejected by the remote provider — the server explicitly said the request is unauthorized.

Source

Thrown at packages/mnemopi/src/core/local-llm.ts:366

	const fetchImpl = options.fetch ?? fetch;
	try {
		// withAuth re-resolves the key on 401 (force-refresh, then sibling
		// rotation) when the configured key is a resolver. An empty static key
		// attempts without an Authorization header (local/proxy setups).
		const response = await withAuth(llmApiKey(), async key => {
			const headers: Record<string, string> = { "Content-Type": "application/json" };
			if (key !== "") {
				headers.Authorization = `Bearer ${key}`;
			}
			const res = await fetchWithRetry(`${baseUrl}/chat/completions`, {
				method: "POST",
				headers,
				body,
				signal: AbortSignal.timeout(60000),
				fetch: fetchImpl,
			});
			if (res.status === 401) {
				throw new ProviderHttpError("mnemopi remote LLM request unauthorized (401)", 401, { headers: res.headers });
			}
			return res;
		});
		if (!response.ok) {
			return null;
		}
		const data = (await response.json()) as {
			choices?: Array<{ message?: { content?: unknown } }>;
		};
		const content = data.choices?.[0]?.message?.content;
		return typeof content === "string" ? content : null;
	} catch {
		return null;
	}
}

export function localGgufAvailable(): false {
	return false;

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the API key/token configured for the remote LLM endpoint is current and re-set it if rotated
  2. Confirm the credential has access to the exact model/endpoint being called
  3. Check that the env var holding the key is actually present in the runtime environment (container/CI often drops it)
  4. Test the key against the endpoint with curl to isolate mnemopi config vs provider-side rejection

Example fix

// before
{ "remoteLlm": { "apiKey": "sk-stale-..." } }
// after
{ "remoteLlm": { "apiKey": "sk-current-..." } }  // or set via env
Defensive patterns

Strategy: try-catch

Validate before calling

const apiKey = process.env.MNEMOPI_REMOTE_LLM_API_KEY;
if (!apiKey || apiKey.length < 8) {
  throw new Error("remote LLM API key missing/short; set MNEMOPI_REMOTE_LLM_API_KEY before calling");
}

Type guard

function hasCredential(cfg: { apiKey?: unknown }): cfg is { apiKey: string } {
  return typeof cfg.apiKey === "string" && cfg.apiKey.length > 0;
}

Try / catch

try {
  const res = await callRemoteLlm(req);
  // ...
} catch (err) {
  if (err instanceof ProviderHttpError && err.status === 401) {
    logger.error("remote LLM rejected credentials; refresh the API key", { url: req.url });
    return null; // or requeue after credential refresh
  }
  throw err;
}

Prevention

When it happens

Trigger: The Authorization header (or equivalent) built from the configured API key/token is missing, expired, revoked, or scoped to a different endpoint when callRemoteLlm performs its fetch with a 60s timeout; any remote LLM call where res.status === 401.

Common situations: Rotated or mistyped API key in mnemopi config; expired token after SSO/session rotation; pointing the remote endpoint at a provider that doesn't accept the configured key; environment variable not set in the deployment (CI, container).

Understand the failure class

Related errors


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