{"record":{"id":"2e27949710cee9d7","repo":"mastra-ai/mastra","slug":"factorysecretencryption-unknown-key-id-envelo","errorCode":null,"errorMessage":"[FactorySecretEncryption] Unknown key id \"${envelope.keyId}\".","messagePattern":"\\[FactorySecretEncryption\\] Unknown key id \"(.+?)\"\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/secret-encryption.ts","lineNumber":98,"sourceCode":"      const cipher = createCipheriv(ALGORITHM, primaryKey, iv);\n      const ciphertext = Buffer.concat([cipher.update(JSON.stringify(value), 'utf8'), cipher.final()]);\n      const envelope: SecretEnvelopeV1 = {\n        keyId: config.primary.id,\n        iv: iv.toString('base64url'),\n        ciphertext: ciphertext.toString('base64url'),\n        tag: cipher.getAuthTag().toString('base64url'),\n      };\n      return `${ENVELOPE_PREFIX}${Buffer.from(JSON.stringify(envelope), 'utf8').toString('base64url')}`;\n    },\n\n    async decrypt<T>(value: unknown): Promise<DecryptedFactorySecret<T>> {\n      if (typeof value !== 'string' || !value.startsWith(ENVELOPE_PREFIX)) {\n        return { value: structuredClone(value) as T, needsReencryption: true };\n      }\n\n      const envelope = parseEnvelope(value);\n      const key = keys.get(envelope.keyId);\n      if (!key) throw new Error(`[FactorySecretEncryption] Unknown key id \"${envelope.keyId}\".`);\n\n      try {\n        const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(envelope.iv, 'base64url'));\n        decipher.setAuthTag(Buffer.from(envelope.tag, 'base64url'));\n        const plaintext = Buffer.concat([\n          decipher.update(Buffer.from(envelope.ciphertext, 'base64url')),\n          decipher.final(),\n        ]).toString('utf8');\n        return {\n          value: JSON.parse(plaintext) as T,\n          needsReencryption: envelope.keyId !== config.primary.id,\n        };\n      } catch {\n        throw new Error('[FactorySecretEncryption] Unable to decrypt encrypted value.');\n      }\n    },\n  };\n}","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/secret-encryption.ts#L80-L116","documentation":"decrypt() looks up envelope.keyId in the configured key map (primary + previous). This error is thrown when the envelope is well-formed but its keyId does not match any configured key, so the ciphertext cannot be decrypted. The library refuses to guess or fall back to brute-force key selection.","triggerScenarios":"Calling decrypt() on a value encrypted with a key whose id is absent from the current config — e.g. the value's keyId was rotated out of `previous`, a different environment's key produced the value, or the keyId string differs by case/whitespace.","commonSituations":"Key rotation where an old key was removed from `previous` before all stored values were re-encrypted; promoting a staging build against a production database (different factory keys per env); restoring a backup encrypted with keys no longer in config; shared database across services with different key sets.","solutions":["Re-add the missing key (with its exact id and material) to `previous` in createFactorySecretEncryption so the value can be decrypted and re-encrypted under the primary key.","Rotate the data: if the key material is truly lost, the values are unrecoverable — reset/regenerate the affected secrets and store fresh encryptions.","Confirm the environment is using the same key config that encrypted the data (check env vars/config for the deployment).","Verify the keyId in stored envelopes (decode the envelope) and reconcile with your key inventory before deployment."],"exampleFix":"// before (old key dropped during rotation)\ncreateFactorySecretEncryption({ primary: { id: 'k2', key } });\n// after (keep old key decrypt-only until rotated)\ncreateFactorySecretEncryption({\n  primary: { id: 'k2', key },\n  previous: [{ id: 'k1', key: oldKey }],\n});","handlingStrategy":"validation","validationCode":"const keyIds = new Set([config.primary.id, ...(config.previous ?? []).map(k => k.id)]);\nfunction keyIdIsConfigured(envelopeKeyIds: string[]): boolean {\n  return envelopeKeyIds.every(id => keyIds.has(id));\n}\n// e.g. scan stored envelopes and list unknown ids before deploying a rotation","typeGuard":null,"tryCatchPattern":"try {\n  return await encryption.decrypt(stored);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('Unknown key id')) {\n    const keyId = err.message.match(/\"([^\"]+)\"/)?.[1];\n    logger.error('Secret encrypted with unconfigured key', { keyId });\n    return null; // or fall back to prompting for the secret\n  }\n  throw err;\n}","preventionTips":["During rotation, keep every retired key in `previous` until a migration has re-encrypted all stored values.","Use environment-specific key sets only with environment-specific data; never share a database across envs with different keys.","Back up key material (e.g. in a KMS) so restored data always has its decrypting keys available.","Before deploying, decode stored envelopes and assert every keyId exists in the new config."],"tags":["encryption","key-management","rotation","configuration"],"backgroundTag":"unknown-key-id","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}