{"record":{"id":"ac9677fe4453af03","repo":"can1357/oh-my-pi","slug":"z-ai-key-provisioning-returned-no-apikey","errorCode":null,"errorMessage":"Z.ai key provisioning returned no apiKey","messagePattern":"Z\\.ai key provisioning returned no apiKey","errorType":"exception","errorClass":"AIError.OAuthError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/registry/oauth/zai.ts","lineNumber":192,"sourceCode":"\t\tthrow new AIError.OAuthError(\"Z.ai key provisioning failed: no organization/project on account\", {\n\t\t\tkind: \"token-exchange\",\n\t\t\tprovider: \"zai\",\n\t\t});\n\t}\n\n\tconst keysUrl = `${BIZ_BASE}/api/biz/v1/organization/${organizationId}/projects/${projectId}/api_keys`;\n\tconst existing = asKeyArray(unwrapEnvelope(await getJson(keysUrl, auth, fetchImpl), \"api key list\")).find(\n\t\tkey => key.name === KEY_NAME,\n\t);\n\tconst keyRecord =\n\t\texisting ??\n\t\t(unwrapEnvelope(await postJson(keysUrl, { name: KEY_NAME }, auth, fetchImpl), \"api key create\") as\n\t\t\t| Record<string, unknown>\n\t\t\t| undefined);\n\n\tconst apiKey = trimmedString(keyRecord?.apiKey);\n\tif (!apiKey) {\n\t\tthrow new AIError.OAuthError(\"Z.ai key provisioning returned no apiKey\", {\n\t\t\tkind: \"token-exchange\",\n\t\t\tprovider: \"zai\",\n\t\t});\n\t}\n\n\t// Always fetch the secret via the copy endpoint: list entries mask it\n\t// (`*****abcd`) and the create response's inline secret is not reliable\n\t// across account states, whereas copy always returns the full secret.\n\tconst copied = unwrapEnvelope(\n\t\tawait getJson(`${keysUrl}/copy/${encodeURIComponent(apiKey)}`, auth, fetchImpl),\n\t\t\"api key copy\",\n\t) as { secretKey?: unknown } | undefined;\n\tconst secretKey = trimmedString(copied?.secretKey);\n\tif (!secretKey) {\n\t\tthrow new AIError.OAuthError(\"Z.ai key provisioning returned no secretKey\", {\n\t\t\tkind: \"token-exchange\",\n\t\t\tprovider: \"zai\",\n\t\t});","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/registry/oauth/zai.ts#L174-L210","documentation":"After creating the API key, mintZaiApiKey unwraps the create response and requires a non-empty apiKey string field. If the record lacks it (or it's non-string/empty), OAuthError is thrown. The create call succeeded at HTTP/envelope level but did not return the expected key identifier.","triggerScenarios":"Z.ai key-create endpoint returns data without apiKey (name rejected silently, async creation pending, or response schema changed so the field is named id/key instead).","commonSituations":"Z.ai API version drift renaming the field; creation endpoint returning a job/pending object; permission-limited token that can create but not read key material.","solutions":["Log the raw create response to see the actual field names; update the library or parse the alternative field if Z.ai renamed apiKey.","Retry provisioning — transient partial responses can occur; delete orphaned keys afterward to stay under quota.","Verify the token used has full api_keys read/write scope.","Catch AIError.OAuthError and report provisioning as incomplete rather than treating login as failed auth."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function hasApiKeyField(r: unknown): r is { apiKey: string } { return !!r && typeof r === \"object\" && typeof (r as Record<string, unknown>).apiKey === \"string\" && (r as { apiKey: string }).apiKey.length > 0; }","tryCatchPattern":"try { return await mintZaiApiKey(token, fetch); }\ncatch (e) {\n  if (e instanceof AIError.OAuthError && e.message.includes(\"no apiKey\")) {\n    logger.error(\"Z.ai key create succeeded but response lacked apiKey — check API schema/token scope\");\n  }\n  throw e;\n}","preventionTips":["Grant the token full api_keys read scope","Keep the library updated for Z.ai response schema changes","Log raw provisioning responses when debugging"],"tags":["zai","provisioning","api-key","missing-field","api-contract"],"backgroundTag":"missing-credential-in-response","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}