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

${label} was not a JSON object

Error message

${label} was not a JSON object

What it means

Thrown by parseXAITokenResponse when a token payload (device-flow poll success or refresh via credentials) parses as JSON but is not a plain object. The label parameter identifies which exchange failed (e.g. 'xAI device-code token response'). The library requires an object carrying access_token/refresh_token/expires_in before it can build OAuthCredentials.

Source

Thrown at packages/ai/src/registry/oauth/xai-oauth.ts:326

			kind: "validation",
			provider: "xai",
		});
	}

	validateXAIEndpoint(verificationUri, "verification_uri");
	validateXAIEndpoint(verificationUriComplete, "verification_uri_complete");
	return {
		deviceCode,
		userCode,
		verificationUriComplete,
		expiresInSeconds,
		intervalSeconds,
	};
}

function parseXAITokenResponse(payload: unknown, label: string, refreshTokenFallback?: string): OAuthCredentials {
	if (!isRecord(payload)) {
		throw new AIError.OAuthError(`${label} was not a JSON object`, {
			kind: "validation",
			provider: "xai",
		});
	}
	const accessToken = typeof payload.access_token === "string" ? payload.access_token : "";
	const responseRefreshToken = typeof payload.refresh_token === "string" ? payload.refresh_token : "";
	const refreshToken = responseRefreshToken || refreshTokenFallback || "";
	const expiresInSeconds = payload.expires_in;
	if (!accessToken) {
		throw new AIError.OAuthError(`${label} missing access_token`, {
			kind: "validation",
			provider: "xai",
		});
	}
	if (!refreshToken) {
		throw new AIError.OAuthError(`${label} missing refresh_token`, {
			kind: "validation",
			provider: "xai",

View on GitHub (pinned to 9690622007)

Solutions

  1. Retry the token exchange or re-run device login — transient proxy responses are the usual culprit.
  2. Check middleboxes (corporate proxy, VPN) that may rewrite the token endpoint body.
  3. If using a custom fetchImpl, ensure it returns the parsed JSON value as-is without transformation.
  4. Compare the raw token-endpoint response with curl; if xAI changed the format, update the ai package.

Example fix

// before: assuming the token body is an object
const tokens = await res.json();
saveCredentials(tokens.access_token);
// after: narrow before use
const tokens: unknown = await res.json();
if (tokens === null || typeof tokens !== "object" || Array.isArray(tokens)) {
  throw new Error("token endpoint did not return a JSON object");
}
saveCredentials((tokens as Record<string, unknown>).access_token);
Defensive patterns

Strategy: type-guard

Validate before calling

// preflight on the token endpoint: confirm object-shaped JSON before an exchange
const probe = await fetch(tokenEndpoint, { method: "POST", headers: { Accept: "application/json" } });
const body: unknown = await probe.json();
if (body === null || typeof body !== "object" || Array.isArray(body)) {
  throw new Error("xAI token endpoint returns non-object JSON in this environment; check proxy");
}

Type guard

function isJsonObject(v: unknown): v is Record<string, unknown> {
  return typeof v === "object" && v !== null && !Array.isArray(v);
}

Try / catch

try {
  await xaiProvider.credentials();
} catch (err) {
  if (err instanceof AIError.OAuthError && err.kind === "validation" && err.message.includes("was not a JSON object")) {
    logger.warn("xAI token exchange returned a non-object payload", {});
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: pollXAIDeviceToken receives an HTTP 200 whose JSON body is not an object (array/string/number/null), or the credentials refresh path gets a non-object payload back from the token endpoint; parseXAITokenResponse throws with the exchange's label in the message.

Common situations: Token endpoint returning a JWE string or other non-object token format; proxy mangling the body; custom fetchImpl double-wrapping responses; xAI changing token response encoding.

Related errors


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