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
- Log in with the account that owns the session so the correct decryption key is available.
- Ensure both CLI and server are on compatible versions (legacy vs dataKey encryption path) — upgrade the CLI if the session format is newer.
- Retry from the original machine where the session was created, if key material is device-bound.
- 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
- Keep the CLI up to date so legacy vs dataKey encryption variants are handled.
- Authenticate as the session-owning account before resuming.
- Resume from the original machine when key material is device-bound.
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
- Failed to decrypt data key for Happy session ${session.id}
- Resume session handler not available
- Happy session ID is required: happy resume <session-id>
AI-assisted analysis of slopus/happy@b824cd0a46 (2026-08-31).
Data as JSON: /api/errors/c8bedbb0b00194a4.
Report an issue: GitHub.