slopus/happy · warning · Error
Metadata version mismatch
Error message
Metadata version mismatch
What it means
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.
Source
Thrown at packages/happy-cli/src/api/apiSession.ts:948
skipExistingMessages() {
this.skipInitialMessages = true;
}
updateMetadata(handler: (metadata: Metadata) => Metadata) {
this.metadataLock.inLock(async () => {
await backoff(async () => {
let updated = handler(this.metadata!); // Weird state if metadata is null - should never happen but here we are
const answer = await this.socket.emitWithAck('update-metadata', { sid: this.sessionId, expectedVersion: this.metadataVersion, metadata: encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, updated)) });
if (answer.result === 'success') {
this.metadata = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.metadata));
this.metadataVersion = answer.version;
} else if (answer.result === 'version-mismatch') {
if (answer.version > this.metadataVersion) {
this.metadataVersion = answer.version;
this.metadata = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.metadata));
}
throw new Error('Metadata version mismatch');
} else if (answer.result === 'error') {
// Hard error - ignore
}
});
});
}
/**
* Update session agent state
* @param handler - Handler function that returns the updated agent state
*/
updateAgentState(handler: (metadata: AgentState) => AgentState) {
logger.debugLargeJson('Updating agent state', this.agentState);
this.agentStateLock.inLock(async () => {
await backoff(async () => {
let updated = handler(this.agentState || {});
const answer = await this.socket.emitWithAck('update-state', { sid: this.sessionId, expectedVersion: this.agentStateVersion, agentState: updated ? encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, updated)) : null });
if (answer.result === 'success') {View on GitHub (pinned to b824cd0a46)
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
Defensive patterns
Strategy: retry
Try / catch
try {
await updateSessionMetadata(sessionId, metadata);
} catch (e) {
if (e.message === 'Metadata version mismatch') {
// newer server metadata already adopted; retry once
await updateSessionMetadata(sessionId, metadata);
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Metadata version mismatch
- Agent state version mismatch
- Daemon state version mismatch
- Session not started
- ACP session is not started
AI-assisted analysis of slopus/happy@b824cd0a46 (2026-08-31).
Data as JSON: /api/errors/a4e1f44d9d3e26c9.
Report an issue: GitHub.