{"record":{"id":"dfada1905d3dce1e","repo":"Stirling-Tools/Stirling-PDF","slug":"failed-to-save-authentication-token","errorCode":null,"errorMessage":"Failed to save authentication token","messagePattern":"Failed to save authentication token","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/desktop/services/authService.ts","lineNumber":365,"sourceCode":"        supabaseKey: SUPABASE_KEY,\n        saasServerUrl: STIRLING_SAAS_URL,\n      });\n\n      const {\n        token,\n        username: returnedUsername,\n        email,\n        refresh_token: refreshToken,\n      } = response;\n\n      // Save token to all storage locations. Supabase (SaaS) logins include a\n      // refresh token so the short-lived access token can be renewed; self-hosted\n      // logins return null here and refresh via the current access token instead.\n      try {\n        await this.saveTokenEverywhere(token, refreshToken);\n      } catch (error) {\n        console.error(\"[Desktop AuthService] Failed to save token:\", error);\n        throw new Error(\"Failed to save authentication token\", {\n          cause: error,\n        });\n      }\n\n      // Save user info to store\n      await invoke(\"save_user_info\", {\n        username: returnedUsername || username,\n        email,\n      });\n\n      const userInfo: UserInfo = {\n        username: returnedUsername || username,\n        email: email || undefined,\n      };\n\n      this.setAuthStatus(\"authenticated\", userInfo);\n\n      return userInfo;","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/desktop/services/authService.ts#L347-L383","documentation":"Thrown by AuthService.login after the Rust 'login' invoke command already returned a valid token+refresh_token, but saveTokenEverywhere() failed to persist the token into the Tauri secure store / localStorage. The login was successful server-side; only local persistence failed. The underlying storage-layer error is preserved as error.cause, and auth status is NOT changed by this throw (the outer catch will reset it to unauthenticated).","triggerScenarios":"saveTokenEverywhere(token, refreshToken) rejects. Concretely: the Tauri store plugin command fails to write, the OS keyring/credential store is locked or missing, the store JSON is corrupted, the disk is full, or a keychain permission prompt was declined.","commonSituations":"Running on a headless/CI Linux box with no secret-service daemon; a corporate machine where the login keychain is locked at the moment of login; a corrupted stirling_jwt / Tauri store file left from an older app version; disk-full on the volume holding the Tauri app data dir.","solutions":["Inspect error.cause for the real storage-layer message (Tauri store write vs keyring access) and fix that specific layer.","On Linux, ensure a secret-service implementation (gnome-keyring / kwallet) is running and unlocked before launching the app.","Clear a corrupted store: remove the Tauri store file and localStorage key 'stirling_jwt', then retry login.","If the keyring is permanently unavailable, reinstall the desktop app so the Tauri store/keyring plugins re-register.","Free disk space on the volume holding the OS temp / app-data directory."],"exampleFix":"// before: caller sees only the generic message\ntry { await authService.login(...); }\ncatch (e) { showError(e.message); }\n\n// after: surface the real storage cause and offer retry\ntry { await authService.login(...); }\ncatch (e) {\n  if (e instanceof Error && e.message === 'Failed to save authentication token') {\n    showError(`Could not store your login: ${e.cause instanceof Error ? e.cause.message : e.cause}`);\n    offerRetry();\n    return;\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isTokenSaveFailure(e: unknown): e is Error & { cause: unknown } {\n  return e instanceof Error && e.message === 'Failed to save authentication token';\n}","tryCatchPattern":"try { await authService.login(server, user, pass); }\ncatch (e) {\n  if (isTokenSaveFailure(e)) {\n    // login worked server-side; only local persistence failed -> safe to retry login\n    const cause = e instanceof Error ? (e as any).cause : undefined;\n    reportStorageError(cause);\n    return;\n  }\n  throw e;\n}","preventionTips":["Verify the OS keyring/credential store is available before showing the login form on desktop.","Clear corrupted Tauri store / localStorage on startup if a version migration is detected.","Never swallow error.cause — it is the only indicator of which storage layer failed."],"tags":["authentication","desktop","storage","tauri","keyring"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}