{"record":{"id":"eaf76d0e45c69fde","repo":"mastra-ai/mastra","slug":"factorysecretencryption-invalid-encrypted-value","errorCode":null,"errorMessage":"[FactorySecretEncryption] Invalid encrypted value.","messagePattern":"\\[FactorySecretEncryption\\] Invalid encrypted value\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/secret-encryption.ts","lineNumber":49,"sourceCode":"  primary: FactorySecretEncryptionKey;\n  previous?: FactorySecretEncryptionKey[];\n}\n\nfunction validateKey({ id, key }: FactorySecretEncryptionKey): Buffer {\n  if (!id) throw new Error('[FactorySecretEncryption] Key id is required.');\n  const buffer = Buffer.from(key);\n  if (buffer.byteLength !== 32) {\n    throw new Error(`[FactorySecretEncryption] Key \"${id}\" must be exactly 32 bytes.`);\n  }\n  return buffer;\n}\n\nfunction parseEnvelope(value: string): SecretEnvelopeV1 {\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(Buffer.from(value.slice(ENVELOPE_PREFIX.length), 'base64url').toString('utf8'));\n  } catch {\n    throw new Error('[FactorySecretEncryption] Invalid encrypted value.');\n  }\n\n  if (\n    !parsed ||\n    typeof parsed !== 'object' ||\n    typeof (parsed as SecretEnvelopeV1).keyId !== 'string' ||\n    typeof (parsed as SecretEnvelopeV1).iv !== 'string' ||\n    typeof (parsed as SecretEnvelopeV1).ciphertext !== 'string' ||\n    typeof (parsed as SecretEnvelopeV1).tag !== 'string'\n  ) {\n    throw new Error('[FactorySecretEncryption] Invalid encrypted value.');\n  }\n  return parsed as SecretEnvelopeV1;\n}\n\n/**\n * Creates a versioned AES-256-GCM encryptor. The primary key is used for new\n * writes; previous keys remain decrypt-only until stored values are rotated.","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/secret-encryption.ts#L31-L67","documentation":"parseEnvelope decodes the base64url payload after the envelope prefix and JSON-parses it. This error is thrown when the value either is not valid base64url, or does not decode into a UTF-8 string that is valid JSON. The library throws it to fail fast on encrypted values that are structurally unreadable before any key lookup or decryption is attempted.","triggerScenarios":"Calling decrypt() (via envelope) with a string that carries the ENVELOPE_PREFIX but whose remainder is not valid base64url (e.g. contains '+', '/', or '=' from a different base64 encoding), or decodes to non-JSON text such as a raw secret, truncated value, or swapped-in plaintext.","commonSituations":"Values mangled by copy/paste or URL encoding, secrets stored with standard base64 instead of base64url, a value truncated by a column size limit or log sanitization, or someone pasting a plaintext/other-format value where an encrypted envelope was expected.","solutions":["Re-encrypt the value with the same library version's encrypt() so it produces a fresh valid envelope; the corrupted value cannot be repaired.","Verify the stored value retains the exact envelope prefix and only base64url characters (A-Z a-z 0-9 - _), re-encoding with Buffer.from(x).toString('base64url') if it was stored as standard base64.","Check for transport/storage corruption: compare the stored string byte-for-byte with what encrypt() returned (length, truncation, whitespace trimming).","Wrap decrypt() in try/catch and fall back to treating the value as plaintext (e.g. needsReencryption flow) if the value may predate encryption."],"exampleFix":"// before (standard base64 stored value passed as-is)\ndecrypt(valueFromDb);\n// after (normalize encoding before decrypt)\nconst normalized = Buffer.from(valueFromDb, 'base64').toString('base64url');\nawait encryption.decrypt(normalized);","handlingStrategy":"validation","validationCode":"const PREFIX = 'enc:v1:'; // match ENVELOPE_PREFIX\nfunction looksEncrypted(value: unknown): value is string {\n  if (typeof value !== 'string' || !value.startsWith(PREFIX)) return false;\n  const rest = value.slice(PREFIX.length);\n  return /^[A-Za-z0-9_-]+$/.test(rest) && rest.length > 0;\n}","typeGuard":"function isBase64urlJson(value: string): boolean {\n  try {\n    JSON.parse(Buffer.from(value, 'base64url').toString('utf8'));\n    return true;\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":"try {\n  const secret = await encryption.decrypt(stored);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('Invalid encrypted value')) {\n    // treat as legacy plaintext and re-encrypt, or surface a data-corruption report\n  } else throw err;\n}","preventionTips":["Always store the exact string returned by encrypt() — never re-encode it as standard base64 or trim it.","Add a startup check that decrypts a known canary value to verify storage round-trips base64url untouched.","Validate encrypted columns/fields are stored with a text type large enough and no transformers that alter content.","Keep plaintext values out of fields designated for envelopes; use the explicit plaintext compatibility path instead."],"tags":["encoding","base64url","json","validation"],"backgroundTag":"invalid-encrypted-payload","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}