{"record":{"id":"7c4112a7c9f2308e","repo":"jackwener/OpenCLI","slug":"auth-required-7c4112","errorCode":"AUTH_REQUIRED","errorMessage":"Not authenticated. Run: opencli spotify auth","messagePattern":"Not authenticated\\. Run: opencli spotify auth","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/spotify/spotify.js","lineNumber":70,"sourceCode":"        body: new URLSearchParams({ grant_type: 'refresh_token', refresh_token: refreshToken }),\n    });\n    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) {","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/spotify/spotify.js#L52-L88","documentation":"getToken loads persisted tokens from disk and throws AUTH_REQUIRED when no token file exists at all. Spotify API calls require an access token, and the CLI will not silently start an auth flow, so it fails fast with the exact command needed to authenticate.","triggerScenarios":"Running any spotify command (e.g. opencli spotify play/search) before ever running opencli spotify auth, or after the token file was deleted from its storage location.","commonSituations":"Fresh install or new machine; CI/container without a persisted token file; token file removed by cleanup or a different HOME/user; typo'd profile so loadTokens looks in the wrong path.","solutions":["Run opencli spotify auth to authenticate and create the token file.","Verify you run commands as the same user/HOME that performed the auth (token files are per-user).","If tokens exist but aren't found, check the token storage path/permissions."],"exampleFix":"// before\nopencli spotify search \"daft punk\"  // AUTH_REQUIRED\n// after\nopencli spotify auth\nopencli spotify search \"daft punk\"","handlingStrategy":"validation","validationCode":"import fs from 'node:fs';\nif (!fs.existsSync(TOKEN_PATH)) { console.error('Not authenticated. Run: opencli spotify auth'); process.exit(1); }","typeGuard":null,"tryCatchPattern":"try {\n  await spotifyCommand(args);\n} catch (e) {\n  if (e.code === 'AUTH_REQUIRED') {\n    await runAuthFlow(); // or instruct: opencli spotify auth\n    await spotifyCommand(args); // retry once after auth\n  } else throw e;\n}","preventionTips":["Run opencli spotify auth as a setup step before scripting against the CLI.","Ensure the same user/HOME is used for auth and commands in CI/containers.","Persist the token file across container restarts via a mounted volume."],"tags":["authentication","spotify","oauth","missing-credentials"],"backgroundTag":"not-authenticated","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}