{"record":{"id":"f0eda3eeb85975c7","repo":"slopus/happy","slug":"server-unavailable-f0eda3","errorCode":null,"errorMessage":"Server unavailable","messagePattern":"Server unavailable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/happy-cli/src/utils/setupOfflineReconnection.ts","lineNumber":93,"sourceCode":" * ```\n */\nexport function setupOfflineReconnection(opts: SetupOfflineReconnectionOptions): SetupOfflineReconnectionResult {\n    const { api, sessionTag, metadata, state, response, onSessionSwap } = opts;\n\n    let session: ApiSessionClient;\n    let reconnectionHandle: ReturnType<typeof startOfflineReconnection<ApiSessionClient>> | null = null;\n\n    // Note: connectionState.notifyOffline() was already called by api.ts with error details\n    if (!response) {\n        // Create a no-op session stub for offline mode using shared utility\n        session = createOfflineSessionStub(sessionTag);\n\n        // Start background reconnection\n        reconnectionHandle = startOfflineReconnection<ApiSessionClient>({\n            serverUrl: configuration.serverUrl,\n            onReconnected: async () => {\n                const resp = await api.getOrCreateSession({ tag: sessionTag, metadata, state });\n                if (!resp) throw new Error('Server unavailable');\n                const realSession = api.sessionSyncClient(resp);\n                // Notify caller to swap the session reference\n                onSessionSwap(realSession);\n                return realSession;\n            },\n            onNotify: (msg) => {\n                // Log to console - this matches Claude's behavior\n                console.log(msg);\n            }\n        });\n\n        return { session, reconnectionHandle, isOffline: true };\n    } else {\n        session = api.sessionSyncClient(response);\n        return { session, reconnectionHandle: null, isOffline: false };\n    }\n}\n","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/slopus/happy/blob/b824cd0a4681d41af631a8e422a813873e4455b0/packages/happy-cli/src/utils/setupOfflineReconnection.ts#L75-L111","documentation":"setupOfflineReconnection starts a background reconnection loop; when connectivity returns, onReconnected calls api.getOrCreateSession to re-acquire the session. If the API responds but returns a falsy response (session could not be created/retrieved), it throws 'Server unavailable' to signal the swap to a live session failed. It represents a server-side refusal/empty response rather than a client network error.","triggerScenarios":"After offline reconnection succeeds at the transport level, api.getOrCreateSession({ tag, metadata, state }) resolves to null/undefined — e.g. server returned an empty body, an error envelope parsed to nothing, or the session could not be re-created.","commonSituations":"Server under load returning empty/failed responses after an outage; session tag/state rejected silently; API version mismatch so the response shape isn't what the client expects; load balancer returning 200 with empty body.","solutions":["Retry — reconnection may succeed once the server stabilizes; the reconnection loop usually re-attempts.","Inspect server logs / API health for why getOrCreateSession returned no session for that tag.","Verify client and server versions agree on the getOrCreateSession response shape.","Restart the CLI session; if the server truly lost the session, create a fresh one."],"exampleFix":"// before\nconst resp = await api.getOrCreateSession({ tag: sessionTag, metadata, state });\nif (!resp) throw new Error('Server unavailable');\n// after\nconst resp = await api.getOrCreateSession({ tag: sessionTag, metadata, state });\nif (!resp) throw new Error(`Server did not return a session for tag \"${sessionTag}\"; check server health and retry`);","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"reconnectionHandle = startOfflineReconnection<ApiSessionClient>({\n  ...opts,\n  onReconnected: async () => {\n    for (let attempt = 1; attempt <= 3; attempt++) {\n      const resp = await api.getOrCreateSession({ tag: sessionTag, metadata, state });\n      if (resp) return api.sessionSyncClient(resp);\n      await new Promise(r => setTimeout(r, 500 * attempt));\n    }\n    throw new Error('Server unavailable after retries');\n  },\n});","preventionTips":["Retry getOrCreateSession with backoff after reconnection; servers may briefly return empty responses post-outage.","Confirm client/server API versions match on the response shape.","Monitor server health and alert on sessions failing to re-acquire.","Provide a user-facing fallback (create a fresh session) when re-acquisition repeatedly fails."],"tags":["network","reconnection","api","server"],"backgroundTag":"server-unavailable","analyzedSha":"b824cd0a4681d41af631a8e422a813873e4455b0","analyzedAt":"2026-08-31T23:12:36.205Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}