{"record":{"id":"67071261d2d921fe","repo":"actualbudget/actual","slug":"token-expired-670712","errorCode":"token-expired","errorMessage":"Authentication failed: invalid or expired session token","messagePattern":"Authentication failed: invalid or expired session token","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/main.ts","lineNumber":304,"sourceCode":"  await sqlite.init();\n  asyncStorage.init({ persist: false });\n  await fs.init();\n  fs._setDocumentDir(dataDir || process.cwd());\n\n  if (serverURL) {\n    setServer(serverURL);\n\n    if ('sessionToken' in config && config.sessionToken) {\n      // Session token authentication\n      await runHandler(handlers['subscribe-set-token'], {\n        token: config.sessionToken,\n      });\n      // Validate the token\n      const user = await runHandler(handlers['subscribe-get-user'], undefined);\n      if (!user || user.tokenExpired === true) {\n        // Clear invalid token\n        await runHandler(handlers['subscribe-set-token'], { token: '' });\n        throw withErrorCode(\n          new Error('Authentication failed: invalid or expired session token'),\n          'token-expired',\n        );\n      }\n      if (user.offline === true) {\n        // Clear token since we can't validate\n        await runHandler(handlers['subscribe-set-token'], { token: '' });\n        throw withErrorCode(\n          new Error('Authentication failed: server offline or unreachable'),\n          'network-failure',\n        );\n      }\n    } else if ('password' in config && config.password) {\n      const result = await runHandler(handlers['subscribe-sign-in'], {\n        password: config.password,\n      });\n      if (result?.error) {\n        // `result.error` is already a machine-readable slug (e.g.","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/main.ts#L286-L322","documentation":"During init(), the previously stored session token is validated via 'subscribe-get-user'. If the user is missing or tokenExpired is true, the stale token is cleared and this error is thrown with code 'token-expired'. It means the server explicitly reported the token as expired or invalid, so the client must re-authenticate.","triggerScenarios":"Calling init() (directly or via initApp) with SERVER_TOKEN/auth config set to a token the server no longer recognizes: token expired server-side, server restarted with different secret, or the budget/user was deleted.","commonSituations":"Long-lived desktop session reconnecting after token TTL passed; pointing a client at a different sync server than the one that issued the token; server database reset or user removed.","solutions":["Re-authenticate: clear the stored token (the code already clears it) and call init again with the server password","Verify the client points at the correct sync server URL for the token it holds","If you run the server, confirm it wasn't reset and its signing secret is stable across restarts","Log the user in again through the UI to obtain a fresh token"],"exampleFix":"// before\nawait init({ URL: serverUrl, TOKEN: staleToken });\n// after\nawait init({ URL: serverUrl, password }); // password auth re-derives a fresh token","handlingStrategy":"try-catch","validationCode":"// Check token freshness before init\nconst token = getStoredToken();\nif (!token || isTokenPayloadExpired(token)) {\n  await reauthenticate(); // get fresh token via password sign-in\n}","typeGuard":"function isTokenExpiredError(e: unknown): e is Error & { code: 'token-expired' } {\n  return e instanceof Error && (e as { code?: string }).code === 'token-expired';\n}","tryCatchPattern":"try {\n  await init({ URL: serverUrl, TOKEN: storedToken });\n} catch (e) {\n  if (e instanceof Error && (e as { code?: string }).code === 'token-expired') {\n    await init({ URL: serverUrl, password }); // fall back to password sign-in\n  } else {\n    throw e;\n  }\n}","preventionTips":["Check error.code === 'token-expired' specifically and re-authenticate rather than retrying with the same token","Fall back to password-based init when the token path fails","Keep the sync server URL stable; switching servers invalidates tokens","Monitor server-side token expiry TTL and refresh proactively in long-lived sessions"],"tags":["authentication","session","sync-server","token"],"backgroundTag":"jwt-token-expired","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}