slopus/happy · error
Server unavailable
Error message
Server unavailable
What it means
When the network drops, startOfflineReconnection buffers and, once the server is back, calls api.getOrCreateSession to re-acquire the session. If that call resolves without a session payload, the reconnection callback throws 'Server unavailable' because reconnecting without a valid session record is impossible.
Source
Thrown at packages/happy-cli/src/claude/runClaude.ts:190
metadata,
metadataVersion: parseInt(reconnectMetadataVersion || '0', 10),
agentState: state,
agentStateVersion: parseInt(reconnectAgentStateVersion || '0', 10),
};
} else {
response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
}
// Handle server unreachable case - run Claude locally with hot reconnection
// Note: connectionState.notifyOffline() was already called by api.ts with error details
if (!response) {
let offlineSessionId: string | null = null;
const reconnection = startOfflineReconnection({
serverUrl: configuration.serverUrl,
onReconnected: async () => {
const resp = await api.getOrCreateSession({ tag: randomUUID(), metadata, state });
if (!resp) throw new Error('Server unavailable');
const session = api.sessionSyncClient(resp);
let latestClaudeGoalStatus: AgentGoalStatus | null = null;
const observedClaudeGoalRevisions = new Set<string>();
const goalCommandSupported = () => {
const slashCommands = session.getMetadata()?.slashCommands ?? [];
return slashCommands.includes('goal') || slashCommands.includes('/goal');
};
const currentClaudeSessionId = () => session.getMetadata()?.claudeSessionId ?? null;
const updateClaudeGoalState = (event: ClaudeGoalStatusTranscriptEvent) => {
if (observedClaudeGoalRevisions.has(event.sourceRevision)) {
return;
}
const capabilities = claudeGoalActionCapabilities({
goalCommandSupported: goalCommandSupported(),
observedGoalStatus: true,
confirmedActions: CLAUDE_GOAL_ACTION_CONFIRMATIONS,
});
const goalStatus = mapClaudeGoalStatusEventToAgentGoalStatus(View on GitHub (pinned to b824cd0a46)
Solutions
- Retry the reconnection once the server is fully healthy (check GET /health on configuration.serverUrl).
- Check server logs around the reconnect timestamp for the failed getOrCreateSession call.
- Verify no proxy/load balancer is swallowing the request with an empty success response.
- Restart the happy session — offline state is persisted, so it can be re-synced.
- Confirm server version compatibility with the CLI's API version.
Example fix
// before
const resp = await api.getOrCreateSession({ tag: randomUUID(), metadata, state });
if (!resp) throw new Error('Server unavailable');
// after
const resp = await api.getOrCreateSession({ tag: randomUUID(), metadata, state });
if (!resp) throw new RetriableError('Server unavailable: getOrCreateSession returned no session during reconnection'); Defensive patterns
Strategy: retry
Validate before calling
const health = await fetch(`${configuration.serverUrl}/health`).then(r => r.ok).catch(() => false);
if (!health) console.warn('Server still unhealthy — deferring reconnection'); Try / catch
const reconnection = startOfflineReconnection({
serverUrl: configuration.serverUrl,
onReconnected: async () => {
for (let attempt = 1; attempt <= 3; attempt++) {
const resp = await api.getOrCreateSession({ tag: randomUUID(), metadata, state });
if (resp) { /* proceed */ return; }
await sleep(attempt * 2000);
}
logger.error('Reconnection failed: server unavailable after 3 attempts');
}
}); Prevention
- Monitor server health before assuming client bugs.
- Avoid proxies that return empty 2xx responses during maintenance.
- Keep CLI and server versions aligned.
- Rely on offline persistence so a failed reconnection is recoverable by restart.
When it happens
Trigger: During offline reconnection, getOrCreateSession({tag, metadata, state}) returns null/undefined — typically because the server is still partially up (accepting connections) but the session API is failing, or the request was answered by a fallback/proxy.
Common situations: Server restarting while the client reconnects mid-restart; reverse proxy returning an empty 2xx during maintenance; the tagged session could not be recreated server-side; flaky network causing half-open reconnections.
Related errors
- Server unavailable
- Authentication failed
- Happy session lookup authentication expired for legacy accou
- Failed to load Happy sessions: ${error.message}
- Token exchange failed: ${tokenResponse.statusText}
AI-assisted analysis of slopus/happy@b824cd0a46 (2026-08-31).
Data as JSON: /api/errors/b698c3bc63db218c.
Report an issue: GitHub.