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

Authorization was denied

Error message

Authorization was denied

What it means

While polling Kilo's device-code endpoint for approval, an HTTP 403 means the authorization was explicitly denied (or the code was rejected). loginKilo converts this into an OAuthError with kind "device-auth" and this fixed message. Contrast with 202 (still pending) and 410 (expired).

Source

Thrown at packages/ai/src/registry/kilo.ts:70

	callbacks.onAuth?.({
		url: verificationUrl,
		instructions: `Enter code: ${userCode}`,
	});

	const deadline = Date.now() + expiresInSeconds * 1000;
	while (Date.now() < deadline) {
		if (callbacks.signal?.aborted) {
			throw new AIError.LoginCancelledError();
		}

		const pollResponse = await fetchImpl(`${KILO_DEVICE_AUTH_BASE_URL}/codes/${encodeURIComponent(userCode)}`);
		if (pollResponse.status === 202) {
			await Bun.sleep(POLL_INTERVAL_MS);
			continue;
		}
		if (pollResponse.status === 403) {
			throw new AIError.OAuthError("Authorization was denied", { kind: "device-auth", provider: "kilo" });
		}
		if (pollResponse.status === 410) {
			throw new AIError.OAuthError("Authorization code expired. Please try again.", {
				kind: "device-auth",
				provider: "kilo",
			});
		}
		if (!pollResponse.ok) {
			throw new AIError.OAuthError(`Failed to poll device authorization: ${pollResponse.status}`, {
				kind: "polling",
				provider: "kilo",
				status: pollResponse.status,
			});
		}

		const pollData = (await pollResponse.json()) as KiloDeviceAuthPollResponse;
		if (pollData.status === "approved" && pollData.token) {
			return {

View on GitHub (pinned to 9690622007)

Solutions

  1. Restart the login flow (call loginKilo again) to generate a fresh device code and approve it this time.
  2. Confirm you open the exact verificationUrl shown and approve with the intended account.
  3. Check for org/security policies or browser extensions blocking the approval action.
  4. If denials are unexpected, verify the device code was entered correctly and wasn't for someone else's request.

Example fix

// before
await loginKilo(callbacks); // user clicks Deny -> unhandled OAuthError
// after
try {
  await loginKilo(callbacks);
} catch (err) {
  if (AIError.OAuthError.is(err) && err.message === "Authorization was denied") {
    return promptRetry("Authorization denied — start login again to approve.");
  }
  throw err;
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await loginKilo(callbacks);
} catch (err) {
  if (err instanceof AIError.OAuthError && err.message === "Authorization was denied") {
    console.error("Login denied on the verification page — run login again and choose Approve.");
    return null;
  }
  throw err;
}

Prevention

When it happens

Trigger: The poll GET `${KILO_DEVICE_AUTH_BASE_URL}/codes/<userCode>` returns status 403 — the user clicked 'Deny' on the verification page, or the server rejects the code/identity at approval time.

Common situations: User denies the request in the browser by mistake or deliberately; the verification page session doesn't match the requesting account; security software or an admin policy blocks approval.

Related errors


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