slopus/happy · warning · Error

Daemon state version mismatch

Error message

Daemon state version mismatch

What it means

Thrown by updateDaemonState when the server reports 'version-mismatch' for daemon runtime state. As with metadata, the client first adopts the server's newer daemonStateVersion/daemonState, then throws to trigger a retry with the refreshed version. It is an expected conflict signal, not a fatal fault.

Source

Thrown at packages/happy-cli/src/api/apiMachine.ts:422

        await backoff(async () => {
            const updated = handler(this.machine.daemonState);

            const answer = await this.socket.emitWithAck('machine-update-state', {
                machineId: this.machine.id,
                daemonState: encodeBase64(encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, updated)),
                expectedVersion: this.machine.daemonStateVersion
            });

            if (answer.result === 'success') {
                this.machine.daemonState = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(answer.daemonState));
                this.machine.daemonStateVersion = answer.version;
                logger.debug('[API MACHINE] Daemon state updated successfully');
            } else if (answer.result === 'version-mismatch') {
                if (answer.version > this.machine.daemonStateVersion) {
                    this.machine.daemonStateVersion = answer.version;
                    this.machine.daemonState = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(answer.daemonState));
                }
                throw new Error('Daemon state version mismatch'); // Triggers retry
            }
        });
    }

    connect() {
        const serverUrl = configuration.serverUrl.replace(/^http/, 'ws');
        logger.debug(`[API MACHINE] Connecting to ${serverUrl}`);

        this.socket = io(serverUrl, {
            transports: ['websocket'],
            auth: {
                token: this.token,
                clientType: 'machine-scoped' as const,
                machineId: this.machine.id,
                happyClient: `cli-daemon/${configuration.currentCliVersion}`
            },
            path: '/v1/updates',
            reconnection: false,

View on GitHub (pinned to b824cd0a46)

Solutions

  1. Allow the surrounding retry in connect() to re-send with the adopted version
  2. Stop extra daemon instances so only one writer exists
  3. If recurring, re-sync daemon state from the server before pushing updates
Defensive patterns

Strategy: retry

Try / catch

try {
  await updateDaemonState(state);
} catch (e) {
  if (e.message === 'Daemon state version mismatch') {
    // server version already adopted locally; retry converges
    await updateDaemonState(state);
  } else throw e;
}

Prevention

When it happens

Trigger: Concurrent daemon-state updates from two daemon processes or a stale update issued by connect()'s update loop after another writer advanced the version.

Common situations: Duplicate daemon instances; daemon reconnecting after another instance already bumped daemonStateVersion; leftover state updates in flight across restarts.

Related errors


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