can1357/oh-my-pi · error · AIError.OAuthError

Z.ai ${operation} failed: ${envelope.msg ?? `code ${String(e

Error message

Z.ai ${operation} failed: ${envelope.msg ?? `code ${String(envelope.code)}`}

What it means

Z.ai API responses arrive wrapped in an envelope ({code, msg, data} or {success,...}). unwrapEnvelope treats success===false or a non-success code as an application-level failure and throws OAuthError tagged kind=token-exchange, provider=zai, embedding the server's msg (or raw code). This is the server saying the operation itself failed, distinct from transport errors.

Source

Thrown at packages/ai/src/registry/oauth/zai.ts:59

/**
 * Z.ai's `{ code, msg, data, success }` envelope. The OAuth token endpoint
 * signals success with `code: 0`; the biz endpoints (`api.z.ai`) use
 * `code: 200` / `success: true`. Accept both; throw `msg` on failure. Bodies
 * without a status wrapper pass through unchanged.
 */
function isSuccessCode(code: unknown): boolean {
	if (code == null) return true;
	if (typeof code === "number") return code === 0 || code === 200;
	if (typeof code === "string") return code === "0" || code === "200";
	return false;
}

function unwrapEnvelope(body: unknown, operation: string): unknown {
	if (body && typeof body === "object" && ("code" in body || "success" in body)) {
		const envelope = body as { code?: unknown; msg?: string; data?: unknown; success?: unknown };
		if (envelope.success === false || !isSuccessCode(envelope.code)) {
			throw new AIError.OAuthError(`Z.ai ${operation} failed: ${envelope.msg ?? `code ${String(envelope.code)}`}`, {
				kind: "token-exchange",
				provider: "zai",
			});
		}
		return "data" in envelope ? envelope.data : envelope;
	}
	return body;
}

async function getJson(url: string, headers: Record<string, string>, fetchImpl: FetchImpl): Promise<unknown> {
	const response = await fetchImpl(url, {
		method: "GET",
		headers,
		signal: AbortSignal.timeout(30_000),
	});
	const responseBody = await response.text();
	if (!response.ok) {
		throw new AIError.ProviderHttpError(

View on GitHub (pinned to 9690622007)

Solutions

  1. Read envelope.msg in the thrown error for the server's reason and fix accordingly (usually re-auth to get a fresh oauth token).
  2. Re-run the Z.ai OAuth login flow to obtain a new access token, then retry minting the key.
  3. Verify the Z.ai account actually has an organization and default project before exchange.
  4. Catch AIError.OAuthError and inspect the message/kind to distinguish auth problems from provisioning problems.

Example fix

// before
const data = await postJson(BUSINESS_LOGIN_URL, { token: staleAccessToken }, {}, fetch);
// after
try { const data = await postJson(BUSINESS_LOGIN_URL, { token: freshAccessToken }, {}, fetch); }
catch (e) { if (e instanceof AIError.OAuthError) await reloginZai(); throw e; }
Defensive patterns

Strategy: try-catch

Type guard

function isZaiFailureEnvelope(b: unknown): boolean { return !!b && typeof b === "object" && (("code" in b && Number((b as any).code) !== 200) || (b as any).success === false); }

Try / catch

try { const biz = await exchangeZaiToken(oauthToken); }
catch (e) {
  if (e instanceof AIError.OAuthError && e.message.includes("Z.ai")) {
    logger.warn("Z.ai exchange rejected", { msg: e.message });
    return reloginZai();
  }
  throw e;
}

Prevention

When it happens

Trigger: Any Z.ai business-API call whose body carries success:false or a failure code: business login with an invalid/expired oauth token, org/project listing denied for the account, api key create/copy rejected (quota, permissions, name conflict).

Common situations: OAuth access token expired between login and exchange; account lacks organization/project provisioning; Z.ai-side rate limits or permission errors; API version changes altering the code semantics.

Related errors


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