{"record":{"id":"4b04b827a0b301fb","repo":"can1357/oh-my-pi","slug":"umans-usage-endpoint-returned-response-status","errorCode":null,"errorMessage":"Umans usage endpoint returned ${response.status} ${response.statusText}","messagePattern":"Umans usage endpoint returned (.+?) (.+?)","errorType":"http","errorClass":"ProviderHttpError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/usage/umans.ts","lineNumber":214,"sourceCode":"\tconst credential = params.credential;\n\tif (credential.type !== \"api_key\" || !credential.apiKey) return null;\n\n\tconst baseUrl = normalizeBaseUrl(params.baseUrl);\n\tconst url = `${baseUrl}${USAGE_PATH}`;\n\tconst headers: Record<string, string> = {\n\t\tauthorization: `Bearer ${credential.apiKey}`,\n\t\taccept: \"application/json\",\n\t};\n\n\tlet payload: UmansUsagePayload | null = null;\n\ttry {\n\t\tconst response = await ctx.fetch(url, { headers, signal: params.signal });\n\t\tif (!response.ok) {\n\t\t\t// Auth failures (401/403) must throw so checkCredentials flags the bad\n\t\t\t// key as ok:false rather than ok:null (unknown). Other non-ok statuses\n\t\t\t// are transient — return null so the probe reports \"no data\".\n\t\t\tif (response.status === 401 || response.status === 403) {\n\t\t\t\tthrow new ProviderHttpError(\n\t\t\t\t\t`Umans usage endpoint returned ${response.status} ${response.statusText}`.trim(),\n\t\t\t\t\tresponse.status,\n\t\t\t\t);\n\t\t\t}\n\t\t\tctx.logger?.warn(\"Umans usage fetch failed\", { status: response.status, statusText: response.statusText });\n\t\t\treturn null;\n\t\t}\n\t\tconst json = (await response.json()) as unknown;\n\t\tif (!isRecord(json)) {\n\t\t\tctx.logger?.warn(\"Umans usage response was not a JSON object\");\n\t\t\treturn null;\n\t\t}\n\t\tpayload = json as unknown as UmansUsagePayload;\n\t} catch (error) {\n\t\t// Re-throw auth errors so the credential-health probe can surface them.\n\t\tif (error instanceof ProviderHttpError) throw error;\n\t\tctx.logger?.warn(\"Umans usage fetch error\", { error: String(error) });\n\t\treturn null;","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/usage/umans.ts#L196-L232","documentation":"Thrown by fetchUmansUsage when the Umans usage endpoint returns 401 (invalid key) or 403 (forbidden). As with OpenCode Go, throwing is intentional so credential probing distinguishes a definitively bad key (ok:false) from transient failures, which return null instead. The message carries the HTTP status and status text.","triggerScenarios":"Usage fetch or checkCredentials against the Umans endpoint with a missing, expired, or revoked API key (401), or a key lacking permission/entitlement for usage data (403).","commonSituations":"UMANS_API_KEY not configured in the environment; key invalidated after team/workspace changes; account without the entitlement that exposes usage; key from a different environment (staging vs production).","solutions":["For 401, obtain and configure a fresh Umans API key.","For 403, confirm the account/role has permission to read usage data or the required subscription.","Verify the key matches the intended Umans environment (staging keys fail in production).","Trim stray whitespace/quotes from the key value in env or config."],"exampleFix":"// before: unconfigured\nconst params = { /* no apiKey */ };\n// after\nconst params = { apiKey: process.env.UMANS_API_KEY! };\nif (!params.apiKey) throw new Error(\"UMANS_API_KEY is not set\");","handlingStrategy":"validation","validationCode":"const key = process.env.UMANS_API_KEY?.trim();\nif (!key) throw new Error(\"UMANS_API_KEY is required\");\n// verify entitlement once at boot\nconst cred = await umansProvider.checkCredentials();\nif (cred.ok === false) throw new Error(\"Umans key rejected (401/403): check key and permissions\");","typeGuard":null,"tryCatchPattern":"try {\n\tconst usage = await umansProvider.fetchUsage(params);\n} catch (err) {\n\tif (err instanceof AIError.ProviderHttpError && (err.status === 401 || err.status === 403)) {\n\t\t// definitive credential failure — disable integration, alert operator\n\t} else throw err; // null return means transient; safe to ignore\n}","preventionTips":["Fail fast on missing key at config load time.","Match key to environment (staging vs production).","Re-validate credentials after team/workspace permission changes."],"tags":["auth","api-key","http-401","http-403","umans"],"backgroundTag":"invalid-api-key","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}