{"record":{"id":"52853f10482522f4","repo":"BloopAI/vibe-kanban","slug":"not-authenticated","errorCode":null,"errorMessage":"Not authenticated","messagePattern":"Not authenticated","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/web-core/src/shared/lib/relayBackendApi.ts","lineNumber":133,"sourceCode":"  hostId: string,\n  sessionId: string,\n  path: string,\n  options: RequestInit = {}\n): Promise<Response> {\n  const baseUrl = buildRemoteSessionBaseUrl(hostId, sessionId);\n  return makeAuthenticatedRequest(baseUrl, path, options);\n}\n\nasync function makeAuthenticatedRequest(\n  baseUrl: string,\n  path: string,\n  options: RequestInit = {},\n  retryOn401 = true\n): Promise<Response> {\n  const authRuntime = getAuthRuntime();\n  const token = await authRuntime.getToken();\n  if (!token) {\n    throw new Error('Not authenticated');\n  }\n\n  const headers = new Headers(options.headers ?? {});\n  if (!headers.has('Content-Type')) {\n    headers.set('Content-Type', 'application/json');\n  }\n  headers.set('Authorization', `Bearer ${token}`);\n  headers.set('X-Client-Version', __APP_VERSION__);\n  headers.set('X-Client-Type', 'frontend');\n\n  const response = await fetch(`${baseUrl}${path}`, {\n    ...options,\n    headers,\n    credentials: 'include',\n  });\n\n  if (response.status === 401 && retryOn401) {\n    const newToken = await authRuntime.triggerRefresh();","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/packages/web-core/src/shared/lib/relayBackendApi.ts#L115-L151","documentation":"makeAuthenticatedRequest calls the auth runtime's getToken() before issuing any relay backend request; if no token is available it throws 'Not authenticated' instead of sending an unauthenticated request. Note this fires only when there is no token at all — an expired token yields a 401 and a different error ('Session expired. Please log in again.').","triggerScenarios":"Calling any relay backend API (e.g. refreshRelaySigningSession) before the user has logged in; getToken() returning null because the session was never established or was cleared from storage; app loaded directly into a page requiring auth without a login redirect.","commonSituations":"Deep-linking to a relay session page without an active session; auth storage cleared by logout in another tab; app opened before the auth provider finished initializing and getToken resolves null.","solutions":["Redirect the user to the login flow before invoking authenticated relay APIs.","Check authRuntime.getToken() (or an isAuthenticated flag) before calling, and skip/queue the request if null.","Await auth initialization/hydration so getToken doesn't return null during startup.","After login, retry the request; the runtime's triggerRefresh only helps with expired tokens, not missing ones."],"exampleFix":"// before: calling the API unconditionally\nconst res = await refreshRelaySigningSession(hostId, sessionId, payload);\n// after: guard on token presence\nconst token = await getAuthRuntime().getToken();\nif (!token) {\n  redirectToLogin();\n  return;\n}\nconst res = await refreshRelaySigningSession(hostId, sessionId, payload);","handlingStrategy":"try-catch","validationCode":"const token = await getAuthRuntime().getToken();\nif (!token) {\n  redirectToLogin();\n  throw new Error('Skipped: no auth token');\n}","typeGuard":"function hasToken(t: string | null | undefined): t is string {\n  return typeof t === 'string' && t.length > 0;\n}","tryCatchPattern":"try {\n  const res = await makeAuthenticatedRelaySessionRequest(hostId, sessionId, path, opts);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Not authenticated') {\n    redirectToLogin();\n  } else throw e;\n}","preventionTips":["Gate authenticated API calls behind an isAuthenticated check or auth-ready promise","Redirect to login when getToken() returns null instead of proceeding","Distinguish 'no token' from 'expired token' (401) — only the latter is fixable via triggerRefresh","Handle multi-tab logout clearing shared auth storage"],"tags":["auth","authentication","relay","session"],"backgroundTag":"not-authenticated","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}