slopus/happy · error

Failed to decrypt metadata for Happy session ${session.id}

Error message

Failed to decrypt metadata for Happy session ${session.id}

What it means

decryptSessionMetadata decrypts the session's encrypted metadata blob using the resolved RecordEncryption (either a session data key or the legacy account key pair). If decryption yields null for either path, the metadata cannot be read with the available keys, and it throws this error naming the session. Without metadata the session cannot be turned into a resumable one.

Source

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

            variant: 'dataKey',
        };
    }

    return {
        key: credentials.secret,
        variant: 'legacy',
    };
}

function decryptSessionMetadata(session: RawSession, credentials: LocalHappyAgentCredentials): Metadata {
    const encryption = resolveSessionEncryption(session, credentials);
    const encryptedMetadata = decodeBase64(session.metadata);
    const metadata = encryption.variant === 'dataKey'
        ? decryptWithDataKey(encryptedMetadata, encryption.key)
        : decryptLegacy(encryptedMetadata, encryption.key);

    if (!metadata) {
        throw new Error(`Failed to decrypt metadata for Happy session ${session.id}`);
    }

    return parseResumableMetadata(session.id, metadata);
}

async function fetchSessions(credentials: LocalHappyAgentCredentials): Promise<RawSession[]> {
    try {
        const response = await axios.get(`${configuration.serverUrl}/v1/sessions`, {
            headers: {
                Authorization: `Bearer ${credentials.token}`,
                'X-Happy-Client': `cli-coding-session/${configuration.currentCliVersion}`,
            },
        });
        return (response.data as { sessions: RawSession[] }).sessions;
    } catch (error) {
        if (error instanceof AxiosError) {
            if (error.response?.status === 401) {
                throw new Error('Happy session lookup authentication expired for legacy account credentials.');

View on GitHub (pinned to b824cd0a46)

Solutions

  1. Log in with the account that owns the session so the correct decryption key is available.
  2. Ensure both CLI and server are on compatible versions (legacy vs dataKey encryption path) — upgrade the CLI if the session format is newer.
  3. Retry from the original machine where the session was created, if key material is device-bound.
  4. If metadata is corrupt server-side, the session is unrecoverable — start a new session.

Example fix

// before
$ happy resume <legacy-session-id>   // old CLI resolves with wrong scheme -> metadata decrypt fails
// after
$ npm i -g slop@latest && happy auth && happy resume <legacy-session-id>
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const session = await resolveHappySession(id);
} catch (err) {
  if ((err as Error).message.startsWith('Failed to decrypt metadata')) {
    // suggest re-login as owning account / CLI upgrade for newer encryption schemes
  } else throw err;
}

Prevention

When it happens

Trigger: decryptWithDataKey or decryptLegacy returns null for session.metadata — wrong key material (dataKey variant key mismatch or legacy secret key from another account), corrupted base64 metadata, or tampered/ciphertext from a different encryption scheme.

Common situations: Mixed legacy/new encryption variants after a library upgrade (session encrypted with legacy scheme but resolved with dataKey or vice versa); wrong account logged in; server data partially migrated; corrupted sync storage.

Related errors


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