can1357/oh-my-pi · error · AIError.OAuthError
Z.ai business login returned no access token
Error message
Z.ai business login returned no access token
What it means
businessLogin exchanges the user's OAuth access token for a business token via the Z.ai business login endpoint. If the unwrapped envelope contains neither access_token nor accessToken (or they are empty/non-string), the flow throws OAuthError (kind=token-exchange, provider=zai). The API succeeded at HTTP level but the response shape lacked the expected credential.
Source
Thrown at packages/ai/src/registry/oauth/zai.ts:135
}
function trimmedString(value: unknown): string | undefined {
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
}
/**
* Exchange the short-lived OAuth access token for a durable biz token via
* ZCode's business-login endpoint. The biz APIs reject the raw OAuth token;
* they require this token.
*/
async function businessLogin(oauthAccessToken: string, fetchImpl: FetchImpl): Promise<string> {
const data = unwrapEnvelope(
await postJson(BUSINESS_LOGIN_URL, { token: oauthAccessToken }, {}, fetchImpl),
"business login",
) as { access_token?: unknown; accessToken?: unknown } | undefined;
const bizToken = trimmedString(data?.access_token) ?? trimmedString(data?.accessToken);
if (!bizToken) {
throw new AIError.OAuthError("Z.ai business login returned no access token", {
kind: "token-exchange",
provider: "zai",
});
}
return bizToken;
}
interface ZaiProject {
projectId?: unknown;
isDefault?: unknown;
}
interface ZaiOrganization {
organizationId?: unknown;
isDefault?: unknown;
projects?: ZaiProject[];
}
/**View on GitHub (pinned to 9690622007)
Solutions
- Confirm the Z.ai account has business/API access enabled and complete any required consent in the Z.ai console.
- Re-run OAuth login to get a fresh token and retry — a partially valid token can yield a tokenless response.
- Log the raw business login response (via fetch interception) to check for renamed fields; upgrade the library if Z.ai changed its schema.
- Catch AIError.OAuthError and surface a clear message directing the user to enable Z.ai business API.
Defensive patterns
Strategy: type-guard
Validate before calling
if (!oauthAccessToken || oauthAccessToken.length < 20) throw new Error("OAuth access token missing/short; re-run Z.ai login first"); Type guard
function hasBizToken(d: unknown): d is { access_token: string } | { accessToken: string } {
if (!d || typeof d !== "object") return false;
const o = d as Record<string, unknown>;
return typeof o.access_token === "string" && o.access_token.length > 0 || typeof o.accessToken === "string" && o.accessToken.length > 0;
} Try / catch
try { const biz = await businessLogin(token, fetch); }
catch (e) { if (e instanceof AIError.OAuthError) { logger.error("Z.ai business login returned no token; check account business-API enablement"); return null; } throw e; } Prevention
- Enable business API access for the Z.ai account before login
- Re-authenticate when exchanges fail repeatedly
- Pin/monitor the library version against Z.ai API schema changes
When it happens
Trigger: Z.ai returns a successful envelope whose data omits access_token — e.g. account not provisioned for business API, consent missing, or Z.ai changed the response field name/version.
Common situations: New Z.ai account without business API enablement; API contract drift on Z.ai side; token valid but scoped to an account type that cannot mint business tokens.
Related errors
- Z.ai ${operation} failed: ${envelope.msg ?? `code ${String(e
- Z.ai key provisioning returned no apiKey
- Kilo device authorization response missing required fields
- GitLab OAuth token exchange failed: ${response.status} ${awa
- Token exchange failed: ${formatOpenAICodexTokenEndpointError
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/9b1c3ad95736bb09.
Report an issue: GitHub.