{"record":{"id":"a4e1f44d9d3e26c9","repo":"slopus/happy","slug":"metadata-version-mismatch-a4e1f4","errorCode":null,"errorMessage":"Metadata version mismatch","messagePattern":"Metadata version mismatch","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/happy-cli/src/api/apiSession.ts","lineNumber":948,"sourceCode":"\n    skipExistingMessages() {\n        this.skipInitialMessages = true;\n    }\n\n    updateMetadata(handler: (metadata: Metadata) => Metadata) {\n        this.metadataLock.inLock(async () => {\n            await backoff(async () => {\n                let updated = handler(this.metadata!); // Weird state if metadata is null - should never happen but here we are\n                const answer = await this.socket.emitWithAck('update-metadata', { sid: this.sessionId, expectedVersion: this.metadataVersion, metadata: encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, updated)) });\n                if (answer.result === 'success') {\n                    this.metadata = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.metadata));\n                    this.metadataVersion = answer.version;\n                } else if (answer.result === 'version-mismatch') {\n                    if (answer.version > this.metadataVersion) {\n                        this.metadataVersion = answer.version;\n                        this.metadata = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.metadata));\n                    }\n                    throw new Error('Metadata version mismatch');\n                } else if (answer.result === 'error') {\n                    // Hard error - ignore\n                }\n            });\n        });\n    }\n\n    /**\n     * Update session agent state\n     * @param handler - Handler function that returns the updated agent state\n     */\n    updateAgentState(handler: (metadata: AgentState) => AgentState) {\n        logger.debugLargeJson('Updating agent state', this.agentState);\n        this.agentStateLock.inLock(async () => {\n            await backoff(async () => {\n                let updated = handler(this.agentState || {});\n                const answer = await this.socket.emitWithAck('update-state', { sid: this.sessionId, expectedVersion: this.agentStateVersion, agentState: updated ? encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, updated)) : null });\n                if (answer.result === 'success') {","sourceCodeStart":930,"sourceCodeEnd":966,"githubUrl":"https://github.com/slopus/happy/blob/b824cd0a4681d41af631a8e422a813873e4455b0/packages/happy-cli/src/api/apiSession.ts#L930-L966","documentation":"Thrown inside ApiSessionClient's metadata update path when the server answers 'version-mismatch' for session metadata. If the server's version is newer, the client decrypts and adopts it locally first; the throw then signals the conflict to the update machinery so the write is retried against the adopted version.","triggerScenarios":"The same session's metadata is written from two places (two devices/CLI instances, or app plus CLI) so the local metadataVersion lags the server's.","commonSituations":"Using happy CLI and mobile/web app on the same session simultaneously; a stale background sync loop retrying an old version; session resumed on another machine between updates.","solutions":["Rely on the built-in retry — the client already adopted the newer server metadata","Avoid simultaneous metadata edits to one session from multiple clients","If it repeats, reload the session state from the server before writing metadata"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await updateSessionMetadata(sessionId, metadata);\n} catch (e) {\n  if (e.message === 'Metadata version mismatch') {\n    // newer server metadata already adopted; retry once\n    await updateSessionMetadata(sessionId, metadata);\n  } else throw e;\n}","preventionTips":["Avoid editing the same session from multiple devices at once","Push metadata promptly instead of batching stale versions","Reload session state from the server after reconnects"],"tags":["version-conflict","optimistic-concurrency","metadata","session"],"backgroundTag":"version-mismatch","analyzedSha":"b824cd0a4681d41af631a8e422a813873e4455b0","analyzedAt":"2026-08-31T23:12:36.205Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}