slopus/happy · error

Happy session ${sessionId} is missing resumable metadata.

Error message

Happy session ${sessionId} is missing resumable metadata.

What it means

parseResumableMetadata validates decrypted session metadata against ResumableMetadataSchema with Zod; if validation fails it discards the Zod details and throws a generic Error saying the session is missing resumable metadata. The decrypted payload exists but doesn't match the expected shape (required fields like path, flavor, ids missing or wrong-typed).

Source

Thrown at packages/happy-cli/src/resume/resolveHappySession.ts:41

    active: boolean;
    metadata: string;
    metadataVersion: number;
    agentState: string | null;
    agentStateVersion: number;
    seq: number;
    dataEncryptionKey: string | null;
};

type RecordEncryption = {
    key: Uint8Array;
    variant: 'legacy' | 'dataKey';
};

export function parseResumableMetadata(sessionId: string, metadata: unknown): Metadata {
    try {
        return ResumableMetadataSchema.parse(metadata) as Metadata;
    } catch {
        throw new Error(`Happy session ${sessionId} is missing resumable metadata.`);
    }
}

export type ResumableHappySession = {
    id: string;
    active: boolean;
    metadata: Metadata;
};

export type ReconnectableHappySession = ResumableHappySession & {
    seq: number;
    metadataVersion: number;
    agentStateVersion: number;
    encryptionKey: Uint8Array;
    encryptionVariant: 'legacy' | 'dataKey';
};

export function resolveSessionRecordByPrefix<T extends { id: string }>(records: T[], sessionId: string): T {

View on GitHub (pinned to b824cd0a46)

Solutions

  1. Upgrade happy-cli so writer and reader schema versions match, then retry
  2. Confirm the session's encryptionKey/encryptionVariant in the sessions file are correct — bad keys yield unparseable plaintext
  3. Inspect the decrypted metadata to find which schema field is missing or mistyped
  4. Delete the broken session record and start a new session to regenerate valid metadata

Example fix

// before (old metadata lacking flavor)
{ path: "/repo", claudeSessionId: "..." } // fails ResumableMetadataSchema
// after: upgrade CLI and re-create session
npm i -g happy-coder@latest
happy  # new session with full metadata
happy resume <session-id>
Defensive patterns

Strategy: try-catch

Validate before calling

import { ResumableMetadataSchema } from './resolveHappySession';
const result = ResumableMetadataSchema.safeParse(metadata);
if (!result.success) {
  console.error('Metadata failed schema:', result.error.issues);
  process.exit(1);
}

Type guard

function isResumableMetadata(m: unknown): m is Metadata {
  return ResumableMetadataSchema.safeParse(m).success;
}

Try / catch

try {
  await handleResumeCommand([sessionId]);
} catch (e) {
  if (e instanceof Error && e.message.includes('missing resumable metadata')) {
    console.error(`${e.message} — likely version skew or bad encryption key; upgrade happy-cli or re-create the session.`);
    process.exit(1);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling parseResumableMetadata with metadata that fails ResumableMetadataSchema.parse — raised from fetchServerMetadata, the metadata getter, or decryptSessionMetadata — e.g. metadata encrypted by an older schema version, decryption producing garbage (wrong encryptionKey/variant), or a field renamed between versions.

Common situations: Version skew between the CLI that wrote the session and the one resuming it; wrong encryption key or variant yielding malformed plaintext that happens to decode but not validate; server metadata absent/stripped; partially written metadata.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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