{"record":{"id":"9e58f6466ea7b540","repo":"paperclipai/paperclip","slug":"missing-login","errorCode":null,"errorMessage":"Missing login","messagePattern":"Missing login","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/local-ai-credentials.ts","lineNumber":36,"sourceCode":"      // authenticated user's isolated login is missing or invalid.\n      let token: string | null = null;\n      if (loginHome) {\n        for (const name of [\".credentials.json\", \"credentials.json\"]) {\n          const raw = await readLocalAiCredentialFile(path.join(loginHome, name)).catch(() => null);\n          if (!raw) continue;\n          let parsed;\n          try { parsed = JSON.parse(raw); } catch { continue; }\n          const value = parsed?.claudeAiOauth?.accessToken;\n          if (typeof value === \"string\" && value.length) { token = value; break; }\n        }\n        // On macOS the CLI stores the isolated login in the auth home's own\n        // suffixed Keychain item rather than a credentials file. The helper\n        // never consults the unsuffixed operator item.\n        if (!token) token = await readIsolatedClaudeKeychainToken(loginHome);\n      } else {\n        token = await readClaudeToken({ allowKeychain: true });\n      }\n      if (!token) throw new Error(\"Missing login\");\n      await fetchClaudeQuota(token);\n      return token;\n    }\n    if (provider === \"openai\") {\n      const auth = await readCodexAuthInfo(loginHome);\n      if (!auth?.accessToken || !auth.refreshToken || !auth.idToken) throw new Error(\"Missing login\");\n      await fetchCodexQuota(auth.accessToken, auth.accountId);\n      return JSON.stringify({ tokens: { access_token: auth.accessToken, refresh_token: auth.refreshToken, id_token: auth.idToken, account_id: auth.accountId }, last_refresh: auth.lastRefresh });\n    }\n    const raw = await fs.readFile(path.join(loginHome!, \"auth.json\"), \"utf8\");\n    const payload = parseGrokAuthPayload(JSON.parse(raw));\n    if (!payload || !hasUsableGrokAuthValue(payload.value)) throw new Error(\"Missing login\");\n    const response = await fetch(\"https://api.x.ai/v1/models\", {\n      headers: { Authorization: `Bearer ${payload.value.key}` },\n      redirect: \"error\", signal: AbortSignal.timeout(15000),\n    });\n    await response.body?.cancel();\n    if (!response.ok) throw new Error(\"Invalid login\");","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/local-ai-credentials.ts#L18-L54","documentation":"For the anthropic provider, readVerifiedLocalAiCredential looks for a Claude login token on disk (or, when a suffixed loginHome is given, an isolated Keychain item). If no token is found it throws 'Missing login' — the user has not completed the provider's local sign-in on this machine.","triggerScenarios":"provider='anthropic' with no Claude credentials file present and (for suffixed homes) no isolated Keychain token; readClaudeToken/readIsolatedClaudeKeychainToken both return null.","commonSituations":"Fresh machine or CI container where `claude auth login` was never run; HOME differs between the login shell and the server process; using an isolated loginHome whose Keychain item was deleted.","solutions":["Run `claude auth login` on the machine running Paperclip, then retry Connect","Confirm HOME/loginHome points at the profile that holds the Claude credentials","For isolated logins, re-create the suffixed Keychain item by signing in for that connection","Run the server under the same OS user that performed the login"],"exampleFix":"// before\n# server runs as svc user; login done as alice → token not found\n// after\nsudo -u svc claude auth login   # then retry Connect","handlingStrategy":"try-catch","validationCode":"import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nconst ready = existsSync(path.join(loginHome ?? os.homedir(), \".claude\", \".credentials.json\")) ||\n  existsSync(path.join(loginHome ?? os.homedir(), \".claude.json\"));\nif (!ready) console.error(\"Run `claude auth login` before connecting\");","typeGuard":null,"tryCatchPattern":"try {\n  await readVerifiedLocalAiCredential({ provider: \"anthropic\", loginHome });\n} catch (e) {\n  if (e instanceof UnprocessableError || /Could not verify the local subscription/.test(String(e?.message))) {\n    showSetupHint(\"Run: claude auth login\");\n  } else throw e;\n}","preventionTips":["Run provider logins on the same machine and OS user as the Paperclip server","Verify HOME/loginHome matches the profile used for sign-in","Pre-flight the login state in setup docs/scripts before calling Connect","For isolated (suffixed) logins, create each Keychain item explicitly"],"tags":["auth","credentials","cli"],"backgroundTag":"missing-credentials","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}