{"record":{"id":"f653d5bee165b816","repo":"mastra-ai/mastra","slug":"invalid-metadata-key-key-keys-must-start-wi","errorCode":null,"errorMessage":"Invalid metadata key: \"${key}\". Keys must start with a letter or underscore and contain only alphanumeric characters and underscores.","messagePattern":"Invalid metadata key: \"(.+?)\"\\. Keys must start with a letter or underscore and contain only alphanumeric characters and underscores\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/storage/domains/memory/base.ts","lineNumber":434,"sourceCode":"\n  /**\n   * Validates metadata keys to prevent SQL injection attacks and prototype pollution.\n   * Keys must start with a letter or underscore, followed by alphanumeric characters or underscores.\n   * @param metadata - The metadata object to validate\n   * @throws Error if any key contains invalid characters or is a disallowed key\n   */\n  protected validateMetadataKeys(metadata: Record<string, unknown> | undefined): void {\n    if (!metadata) return;\n\n    for (const key of Object.keys(metadata)) {\n      // First check for disallowed prototype pollution keys\n      if (DISALLOWED_METADATA_KEYS.has(key)) {\n        throw new Error(`Invalid metadata key: \"${key}\".`);\n      }\n\n      // Then check pattern\n      if (!SAFE_METADATA_KEY_PATTERN.test(key)) {\n        throw new Error(\n          `Invalid metadata key: \"${key}\". Keys must start with a letter or underscore and contain only alphanumeric characters and underscores.`,\n        );\n      }\n\n      // Also limit key length to prevent potential issues\n      if (key.length > MAX_METADATA_KEY_LENGTH) {\n        throw new Error(`Metadata key \"${key}\" exceeds maximum length of ${MAX_METADATA_KEY_LENGTH} characters.`);\n      }\n    }\n  }\n\n  /**\n   * Validates pagination parameters and returns safe offset.\n   * @param page - Page number (0-indexed)\n   * @param perPage - Items per page (0 is allowed and returns empty results)\n   * @throws Error if page is negative, perPage is negative/invalid, or offset would overflow\n   */\n  protected validatePagination(page: number, perPage: number): void {","sourceCodeStart":416,"sourceCodeEnd":452,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/storage/domains/memory/base.ts#L416-L452","documentation":"Mastra storage validates thread metadata keys before persisting or listing threads. A key is rejected if it contains characters other than letters, digits, and underscores, or if it starts with a non-letter/non-underscore character. This guard prevents keys that could break storage backends, query syntax, or serialization.","triggerScenarios":"Calling listThreads (or any code path that invokes validateMetadataKeys) with a thread whose metadata record contains a key like 'my-key', '123abc', 'foo.bar', or an empty string.","commonSituations":"Migrating data from another system that allowed hyphenated or dotted metadata keys; letting user input become metadata keys; generating keys from timestamps or IDs containing dashes or slashes.","solutions":["Rename the offending metadata key to match /^[A-Za-z_][A-Za-z0-9_]*$/ (e.g. 'my-key' -> 'my_key').","Sanitize keys programmatically before writing metadata: replace invalid characters with underscores.","Drop or move invalid keys into a nested value instead of the key itself.","Wrap listThreads/saveThread calls in try-catch and log/repair the offending thread data."],"exampleFix":"// before\nawait memory.storage.listThreads({ metadata: { 'user-id': '42' } });\n// after\nawait memory.storage.listThreads({ metadata: { 'user_id': '42' } });","handlingStrategy":"validation","validationCode":"const SAFE_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;\nfunction assertValidMetadataKeys(metadata) {\n  for (const key of Object.keys(metadata ?? {})) {\n    if (!SAFE_KEY.test(key)) throw new Error(`Invalid metadata key: \"${key}\"`);\n  }\n}","typeGuard":"function hasSafeMetadataKeys(m: unknown): m is Record<string, unknown> {\n  return !!m && Object.keys(m).every(k => /^[A-Za-z_][A-Za-z0-9_]*$/.test(k));\n}","tryCatchPattern":"try {\n  await storage.listThreads(params);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Invalid metadata key')) {\n    // sanitize metadata and retry\n  } else throw e;\n}","preventionTips":["Sanitize keys on write (replace invalid chars with _), never on read","Never use raw user input as metadata keys","Keep metadata keys as fixed literal strings in code"],"tags":["validation","storage","metadata"],"backgroundTag":"schema-validation-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}