decolua/9router · error
Failed to save tokens
Error message
Failed to save tokens
What it means
Thrown by OpenAIService.saveTokens() when the POST of the freshly obtained tokens to the dashboard server (`${server}/api/cli/providers/openai`) returns a non-2xx status. It surfaces the server's own `error` field when present, else the generic fallback message. The OAuth exchange itself succeeded; only persisting the tokens to the local server failed.
Source
Thrown at src/lib/oauth/services/openai.js:84
const response = await fetch(`${server}/api/cli/providers/openai`, {
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,
idToken: tokens.id_token,
scope: tokens.scope,
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to save tokens");
}
return await response.json();
}
/**
* Complete OpenAI OAuth flow
*/
async connect() {
const spinner = createSpinner("Starting OpenAI OAuth...").start();
try {
spinner.text = "Starting local server...";
// Authenticate and get authorization code
const { code, codeVerifier, redirectUri } = await this.authenticate(
"OpenAI",
this.buildOpenAIAuthUrl.bind(this)View on GitHub (pinned to 90b52e06ff)
Solutions
- Read the thrown `error.error` value — it names the server-side reason (401 auth vs 404 route vs 400 validation).
- Re-login to refresh the Bearer token / credentials used by getServerCredentials(); expired sessions are the top cause.
- Confirm the dashboard server is running and that `server` in getServerCredentials() points to the correct host:port.
- Verify server and CLI versions match so the /api/cli/providers/openai endpoint exists and accepts the payload.
- Check server logs for the failing request to see the underlying persistence error.
Example fix
// before
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to save tokens");
}
// after (keep status, survive non-JSON bodies)
if (!response.ok) {
let msg = `Failed to save tokens (HTTP ${response.status})`;
try { const e = await response.json(); if (e && e.error) msg = `${msg}: ${e.error}`; } catch {}
throw new Error(msg);
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before saveTokens, verify server credentials are present and reachable:
const { server, token, userId } = getServerCredentials();
if (!server || !token) throw new Error("Not logged in: missing server credentials");
await fetch(`${server}/api/health`).catch(() => { throw new Error(`Server unreachable at ${server}`); }); Type guard
function isSaveResponse(json) {
return json != null && typeof json === "object" && !json.error;
} Try / catch
try {
await service.saveTokens(tokens);
} catch (err) {
if (err.message === "Failed to save tokens" || /Failed to save tokens/.test(err.message)) {
// re-authenticate CLI session, confirm dashboard server is running, then retry
} else throw err;
} Prevention
- Start the dashboard server before running CLI connect flows.
- Re-login when the CLI session ages out; expired JWTs cause 401s on save.
- Confirm the configured server URL/port matches where the dashboard actually runs.
- Keep CLI and server versions in sync so the providers/openai endpoint exists.
- Save tokens soon after exchange so access/refresh data is complete and valid.
When it happens
Trigger: connect() -> saveTokens(tokens) posts to the server with Bearer token and X-User-Id headers from getServerCredentials(); the server replies !response.ok with a JSON body — auth rejected (expired/invalid JWT or wrong user), server not running at the configured URL, the OpenAI provider endpoint missing, or a validation rejection of the payload.
Common situations: Dashboard server not started or URL/port misconfigured (getServerCredentials pointing at the wrong host); CLI session/JWT expired; user id mismatch between CLI login and server; server version older than the endpoint the CLI calls; database write failure on the server side.
Related errors
- Failed to connect to server
- Failed to save tokens
- loadCodeAssist failed: HTTP ${response.status} ${errorText.s
- onboardUser HTTP ${response.status}: ${errorText.slice(0, 20
- ${data?.message || data?.error || data?.detail || `Codex res
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/db9b9848ce302b40.
Report an issue: GitHub.