Handle Z.AI textual 401 errors for auth diagnostics

This commit is contained in:
William Valentin
2026-02-15 19:50:15 -08:00
parent dd15ccb927
commit d44bfc300b
3 changed files with 20 additions and 3 deletions
+2 -2
View File
@@ -47,14 +47,14 @@
"status": "completed",
"date": "2026-02-16",
"updated": "2026-02-16",
"summary": "Unified Z.AI credential resolution so zhipuai model switches resolve credentials from config, env (including ZAI_API_KEY), and auth store regardless of use_oauth flag. Added clearer Z.AI-specific 401 diagnostics when API auth fails (including missing model.request scope hint).",
"summary": "Unified Z.AI credential resolution so zhipuai model switches resolve credentials from config, env (including ZAI_API_KEY), and auth store regardless of use_oauth flag. Added clearer Z.AI-specific 401 diagnostics when API auth fails (including missing model.request scope hint), including textual 401 errors where the SDK does not expose status.",
"files_modified": [
"src/daemon/models.ts",
"src/models/openai.ts",
"src/daemon/clientFactory.test.ts",
"src/models/openai.test.ts"
],
"test_status": "pnpm test:run src/daemon/clientFactory.test.ts src/models/openai.test.ts + pnpm typecheck passing"
"test_status": "pnpm test:run src/daemon/clientFactory.test.ts src/models/openai.test.ts + pnpm typecheck passing (updated to cover textual 401 without status field)"
},
"deployment-port-env-override": {
+16
View File
@@ -152,4 +152,20 @@ describe('OpenAIClient tool use', () => {
messages: [{ role: 'user', content: 'hello' }],
})).rejects.toThrow(/Z\.AI authentication failed \(401\)/);
});
it('rewrites Z.AI textual 401 errors when SDK status is absent', async () => {
mockCreate.mockRejectedValueOnce(new Error(
'401 You have insufficient permissions for this operation. Missing scopes: model.request.',
));
const client = new OpenAIClient({
apiKey: 'zai-key',
model: 'glm-4.7',
baseURL: 'https://api.z.ai/api/paas/v4',
});
await expect(client.chat({
messages: [{ role: 'user', content: 'hello' }],
})).rejects.toThrow(/The key lacks `model\.request` scope/);
});
});
+2 -1
View File
@@ -260,9 +260,10 @@ export class OpenAIClient implements ModelClient {
const message = error instanceof Error ? error.message : String(error);
const isZai = (this.baseURL ?? '').includes('api.z.ai');
const isUnauthorized401 = status === 401 || /\b401\b/.test(message);
const missingModelRequestScope = message.includes('Missing scopes: model.request');
if (isZai && status === 401) {
if (isZai && isUnauthorized401) {
const hint = missingModelRequestScope
? 'The key lacks `model.request` scope.'
: 'The API key is invalid, expired, or not allowed for this model/endpoint.';