{"record":{"id":"351f28188309f37d","repo":"jackwener/OpenCLI","slug":"auth-corrupted","errorCode":"AUTH_CORRUPTED","errorMessage":"Token file is corrupted. Run: opencli spotify auth","messagePattern":"Token file is corrupted\\. Run: opencli spotify auth","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/spotify/spotify.js","lineNumber":72,"sourceCode":"    if (!res.ok) {\n        const err = await res.json().catch(() => ({}));\n        throw new CliError('REFRESH_FAILED', err?.error_description || `Token refresh failed (${res.status})`);\n    }\n    const data = await res.json();\n    const tokens = {\n        access_token: data.access_token,\n        refresh_token: data.refresh_token || refreshToken,\n        expires_at: Date.now() + data.expires_in * 1000,\n    };\n    saveTokens(tokens);\n    return tokens.access_token;\n}\nasync function getToken() {\n    const tokens = loadTokens();\n    if (!tokens)\n        throw new CliError('AUTH_REQUIRED', 'Not authenticated. Run: opencli spotify auth');\n    if (!tokens.access_token || !tokens.refresh_token || !(tokens.expires_at > 0)) {\n        throw new CliError('AUTH_CORRUPTED', 'Token file is corrupted. Run: opencli spotify auth');\n    }\n    if (Date.now() > tokens.expires_at - 60_000)\n        return refreshAccessToken(tokens.refresh_token);\n    return tokens.access_token;\n}\n// ── Spotify API helper ────────────────────────────────────────────────────────\nasync function api(method, path, body) {\n    const token = await getToken();\n    const res = await fetch(`https://api.spotify.com/v1${path}`, {\n        method,\n        headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n        body: body ? JSON.stringify(body) : undefined,\n    });\n    if (res.status === 204 || res.status === 202)\n        return null;\n    if (!res.ok) {\n        const err = await res.json().catch(() => ({}));\n        throw new CliError('API_ERROR', err?.error?.message || `Spotify API error ${res.status}`);","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/spotify/spotify.js#L54-L90","documentation":"getToken throws AUTH_CORRUPTED when a token file exists but is missing required fields: access_token, refresh_token, or a positive numeric expires_at. The CLI treats a partially-written or malformed token file as unusable because it can neither call the API nor silently refresh, and points the user at re-auth.","triggerScenarios":"loadTokens() returns an object lacking any of access_token, refresh_token, or expires_at > 0 — e.g. a hand-edited, truncated, or partially-written token file, or a file written by an older CLI version with a different schema.","commonSituations":"Manual editing of the token JSON; disk-full or crash mid-write leaving truncated JSON; downgrading/upgrading the CLI to a version with a different token schema; another tool overwriting the token file.","solutions":["Delete the token file and run opencli spotify auth to recreate it cleanly.","Inspect the token file and confirm it contains access_token, refresh_token, and numeric expires_at.","Check for concurrent writers (cron jobs, scripts) that may truncate the file."],"exampleFix":"// before: corrupted file\n{\"access_token\": \"\"}  // AUTH_CORRUPTED\n// after: re-authenticate\nrm ~/.opencli/spotify-tokens.json && opencli spotify auth","handlingStrategy":"validation","validationCode":"const t = loadTokens();\nconst valid = !!t && typeof t.access_token === 'string' && t.access_token.length > 0\n  && typeof t.refresh_token === 'string' && t.refresh_token.length > 0\n  && typeof t.expires_at === 'number' && t.expires_at > 0;\nif (!valid) { console.error('Token file invalid; delete it and run: opencli spotify auth'); }","typeGuard":"function isCompleteTokens(t) {\n  return !!t && typeof t === 'object'\n    && typeof t.access_token === 'string' && t.access_token.length > 0\n    && typeof t.refresh_token === 'string' && t.refresh_token.length > 0\n    && typeof t.expires_at === 'number' && t.expires_at > 0;\n}","tryCatchPattern":"try {\n  await spotifyCommand(args);\n} catch (e) {\n  if (e.code === 'AUTH_CORRUPTED') {\n    fs.rmSync(TOKEN_PATH, { force: true });\n    await runAuthFlow();\n    await spotifyCommand(args);\n  } else throw e;\n}","preventionTips":["Never hand-edit the token file; re-auth instead.","Write tokens atomically (temp file + rename) if customizing storage.","Avoid running multiple processes that rewrite the token file concurrently."],"tags":["authentication","spotify","corrupted-state","oauth"],"backgroundTag":"corrupted-credentials-file","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}