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

  1. Retry the reconnection once the server is fully healthy (check GET /health on configuration.serverUrl).
  2. Check server logs around the reconnect timestamp for the failed getOrCreateSession call.
  3. Verify no proxy/load balancer is swallowing the request with an empty success response.
  4. Restart the happy session — offline state is persisted, so it can be re-synced.
  5. 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

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


AI-assisted analysis of slopus/happy@b824cd0a46 (2026-08-31). Data as JSON: /api/errors/b698c3bc63db218c. Report an issue: GitHub.