{"record":{"id":"67ae2ccfe442e78e","repo":"mastra-ai/mastra","slug":"factorysecretencryption-key-id-is-required","errorCode":null,"errorMessage":"[FactorySecretEncryption] Key id is required.","messagePattern":"\\[FactorySecretEncryption\\] Key id is required\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/secret-encryption.ts","lineNumber":36,"sourceCode":"\n/** Encrypts opaque JSON values before they cross the Factory storage boundary. */\nexport interface FactorySecretEncryption {\n  encrypt<T>(value: T): Promise<string>;\n  decrypt<T>(value: unknown): Promise<DecryptedFactorySecret<T>>;\n}\n\nexport interface FactorySecretEncryptionKey {\n  id: string;\n  key: Uint8Array;\n}\n\nexport interface FactorySecretEncryptionConfig {\n  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' ||","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/secret-encryption.ts#L18-L54","documentation":"Factory secret encryption uses AES-256-GCM with envelope encryption keyed by a stable key id (the id is stamped into envelopes so older keys in `previous` can decrypt). validateKey() throws when the configured key's id is empty/missing, because envelopes could not be attributed or rotated without it.","triggerScenarios":"Calling createFactorySecretEncryption (directly or via primaryKey) with primary (or a previous entry) whose FactorySecretEncryptionKey.id is undefined, null, or an empty string.","commonSituations":"Loading keys from env vars where the id var is unset (missing-env-var); building the config programmatically and forgetting the id field; JSON config with an empty id string; a migration/rotation script that copies only the key bytes.","solutions":["Set a non-empty id (e.g. 'k-2026-01') on the primary FactorySecretEncryptionKey.","If the id comes from an env var (e.g. SECRET_KEY_ID), verify it is set in the deployment environment.","Check that parsed JSON/env config actually populates the id field, not just the key bytes.","When rotating, ensure both old and new keys retain their original stable ids."],"exampleFix":"// before\ncreateFactorySecretEncryption({\n  primary: { id: process.env.SECRET_KEY_ID ?? '', key: keyBytes }, // empty id\n});\n\n// after\nconst id = process.env.SECRET_KEY_ID;\nif (!id) throw new Error('SECRET_KEY_ID must be set');\ncreateFactorySecretEncryption({ primary: { id, key: keyBytes } });","handlingStrategy":"validation","validationCode":"function assertEncryptionKey(cfg: { id?: unknown; key: Uint8Array }): void {\n  if (!cfg.id || typeof cfg.id !== 'string') throw new Error('SECRET_KEY_ID (key id) must be a non-empty string');\n  if (Buffer.from(cfg.key).byteLength !== 32) throw new Error('encryption key must be exactly 32 bytes');\n}\n// run over primary and every previous entry before createFactorySecretEncryption","typeGuard":"function isFactorySecretEncryptionKey(v: unknown): v is { id: string; key: Uint8Array } {\n  return typeof v === 'object' && v !== null && typeof (v as any).id === 'string' && (v as any).id.length > 0 && (v as any).key instanceof Uint8Array;\n}","tryCatchPattern":"try {\n  const enc = createFactorySecretEncryption({ primary, previous });\n} catch (err) {\n  if (err instanceof Error && err.message === '[FactorySecretEncryption] Key id is required.') {\n    console.error('Check SECRET_KEY_ID env var / config: every key needs a stable non-empty id');\n  } else throw err;\n}","preventionTips":["Fail fast at config-load time: assert every key has both a non-empty id and 32 bytes.","Validate required encryption env vars at process startup, before handling requests.","Give keys stable, dated ids (e.g. 'k-2026-01') so rotation keeps decrypting old envelopes.","Add a startup smoke test that constructs the encryption config in CI."],"tags":["encryption","configuration","missing-value","keys"],"backgroundTag":"missing-env-var","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}