decolua/9router · error · Error
Failed to save tokens
Error message
Failed to save tokens
What it means
Thrown by CodexService.saveTokens when the POST to `${server}/api/cli/providers/codex` returns a non-2xx status. It prefers the server's JSON `error` field, falling back to the generic string. The OpenAI/Codex OAuth flow completed — persisting the tokens (accessToken, refreshToken, expiresIn, lastRefreshAt) into the dashboard failed, so the Codex account is not registered.
Source
Thrown at src/lib/oauth/services/codex.js:63
const response = await fetch(`${server}/api/cli/providers/codex`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
"X-User-Id": userId,
},
body: JSON.stringify({
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
expiresIn: tokens.expires_in,
lastRefreshAt: new Date().toISOString(),
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to save tokens");
}
return await response.json();
}
/**
* Complete Codex OAuth flow
*/
async connect() {
const spinner = createSpinner("Starting Codex OAuth...").start();
try {
spinner.text = "Starting local server...";
// Start local server for callback (use fixed port 1455 like real Codex CLI)
const fixedPort = CODEX_CONFIG.fixedPort;
let callbackParams = null;
const { port, close } = await startLocalServer((params) => {View on GitHub (pinned to 90b52e06ff)
Solutions
- Read the server-provided `error` message in the thrown Error for the root cause
- Re-login the CLI to the dashboard to refresh the Bearer token, then retry the Codex connect
- Verify the configured `server` URL/port matches the running 9router dashboard
- Retry connect after confirming the dashboard is healthy and reachable from the CLI
Defensive patterns
Strategy: try-catch
Validate before calling
const { server, token } = getServerCredentials();
if (!server || !token) throw new Error('CLI not authenticated — login to the dashboard before codex connect');
try { new URL(server); } catch { throw new Error(`Invalid server URL in CLI config: ${server}`); } Type guard
function hasValidTarget(c) { return c && typeof c.server === 'string' && /^https?:\/\//.test(c.server) && typeof c.token === 'string' && c.token.length > 0; } Try / catch
try {
await service.saveTokens(tokens);
} catch (e) {
if (/Failed to save tokens/i.test(e.message)) {
// re-auth CLI, confirm server URL/health, then retry — Codex tokens remain valid
} else throw e;
} Prevention
- Keep CLI and dashboard versions aligned so the payload schema matches
- Refresh the CLI session token before starting the browser-based Codex login
- Verify dashboard health before running connect
- Inspect dashboard logs for /api/cli/providers/codex rejection reasons
When it happens
Trigger: Dashboard rejects the save: expired/invalid CLI Bearer token, unreachable or restarting server, wrong `server` URL from getServerCredentials(), or server-side validation rejecting the payload (e.g. missing refreshToken).
Common situations: CLI and dashboard version mismatch causing payload validation failure; CLI token expired during the browser login round-trip; firewall blocking the CLI's localhost POST; pointing the CLI at a different 9router install than the one that issued its credentials.
Related errors
- Failed to save tokens
- Failed to save tokens
- Kiro tool input must be a JSON object
- No Codex access token available. Please re-authorize the con
- HTTP ${response.status}: ${errorMsg}
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/44faeb219643fe4b.
Report an issue: GitHub.