{"record":{"id":"9a1f724317e804a9","repo":"mastra-ai/mastra","slug":"invalid-metadata-key-key","errorCode":null,"errorMessage":"Invalid metadata key: \"${key}\".","messagePattern":"Invalid metadata key: \"(.+?)\"\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/storage/domains/memory/base.ts","lineNumber":429,"sourceCode":"        output[key] = sVal;\n      }\n    }\n    return output;\n  }\n\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.","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/storage/domains/memory/base.ts#L411-L447","documentation":"Thread metadata passed to `listThreads` is validated against a safe-key policy: keys must match a strict pattern (start with a letter or underscore, alphanumeric/underscore only) and must not be disallowed prototype-pollution keys like `__proto__`, `constructor`, or `prototype`. This protects storage from injection/prototype-pollution via metadata keys.","triggerScenarios":"Calling `listThreads({ metadata: { 'my-key': ... } })` (hyphens/dots/spaces fail the pattern) or `{ '__proto__': ..., 'constructor': ... }` (disallowed keys) with `metadata` filters on thread listing.","commonSituations":"Passing user-supplied filter keys straight through from an HTTP query string; using kebab-case keys like `project-id` instead of `project_id`; stale code constructing metadata with reserved JS names.","solutions":["Rename offending metadata keys to match `^[A-Za-z_][A-Za-z0-9_]*$` (e.g. `project-id` → `project_id`).","Remove disallowed keys (`__proto__`, `constructor`, `prototype`) from the metadata object.","Sanitize/whitelist user-supplied metadata keys before passing them to `listThreads`.","Update the stored thread metadata (or migration) so existing records no longer carry invalid keys if stored metadata is also validated."],"exampleFix":"// before\nawait storage.listThreads({ metadata: { 'team-id': 't1', '__proto__': {} } });\n// after\nawait storage.listThreads({ metadata: { team_id: 't1' } });","handlingStrategy":"validation","validationCode":"const SAFE_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;\nconst DISALLOWED = new Set(['__proto__', 'constructor', 'prototype']);\nfunction assertSafeMetadataKeys(metadata: Record<string, unknown> | undefined): void {\n  for (const key of Object.keys(metadata ?? {})) {\n    if (DISALLOWED.has(key) || !SAFE_KEY.test(key)) {\n      throw new Error(`Invalid metadata key: \"${key}\"`);\n    }\n  }\n}\nassertSafeMetadataKeys(filter.metadata);","typeGuard":"function hasSafeMetadataKeys(m: Record<string, unknown>): boolean {\n  const bad = new Set(['__proto__', 'constructor', 'prototype']);\n  return Object.keys(m).every(k => !bad.has(k) && /^[A-Za-z_][A-Za-z0-9_]*$/.test(k));\n}","tryCatchPattern":"try {\n  const threads = await storage.listThreads({ metadata: filter.metadata });\n} catch (e) {\n  if (String((e as Error).message).startsWith('Invalid metadata key')) {\n    throw new BadRequestError('metadata filter keys must be alphanumeric/underscore and not reserved');\n  }\n  throw e;\n}","preventionTips":["Enforce snake_case or camelCase metadata key conventions (no hyphens/dots).","Never pass raw user-supplied keys into metadata filters; whitelist first.","Add schema validation (zod etc.) on metadata key names at API boundaries.","Reject reserved JS names (__proto__, constructor, prototype) in any metadata ingestion path."],"tags":["validation","metadata","prototype-pollution","storage"],"backgroundTag":"invalid-metadata-key","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}