mastra-ai/mastra · error

Invalid metadata key: "${key}".

Error message

Invalid metadata key: "${key}".

What it means

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.

Source

Thrown at packages/core/src/storage/domains/memory/base.ts:429

        output[key] = sVal;
      }
    }
    return output;
  }

  /**
   * Validates metadata keys to prevent SQL injection attacks and prototype pollution.
   * Keys must start with a letter or underscore, followed by alphanumeric characters or underscores.
   * @param metadata - The metadata object to validate
   * @throws Error if any key contains invalid characters or is a disallowed key
   */
  protected validateMetadataKeys(metadata: Record<string, unknown> | undefined): void {
    if (!metadata) return;

    for (const key of Object.keys(metadata)) {
      // First check for disallowed prototype pollution keys
      if (DISALLOWED_METADATA_KEYS.has(key)) {
        throw new Error(`Invalid metadata key: "${key}".`);
      }

      // Then check pattern
      if (!SAFE_METADATA_KEY_PATTERN.test(key)) {
        throw new Error(
          `Invalid metadata key: "${key}". Keys must start with a letter or underscore and contain only alphanumeric characters and underscores.`,
        );
      }

      // Also limit key length to prevent potential issues
      if (key.length > MAX_METADATA_KEY_LENGTH) {
        throw new Error(`Metadata key "${key}" exceeds maximum length of ${MAX_METADATA_KEY_LENGTH} characters.`);
      }
    }
  }

  /**
   * Validates pagination parameters and returns safe offset.

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Rename offending metadata keys to match `^[A-Za-z_][A-Za-z0-9_]*$` (e.g. `project-id` → `project_id`).
  2. Remove disallowed keys (`__proto__`, `constructor`, `prototype`) from the metadata object.
  3. Sanitize/whitelist user-supplied metadata keys before passing them to `listThreads`.
  4. Update the stored thread metadata (or migration) so existing records no longer carry invalid keys if stored metadata is also validated.

Example fix

// before
await storage.listThreads({ metadata: { 'team-id': 't1', '__proto__': {} } });
// after
await storage.listThreads({ metadata: { team_id: 't1' } });
Defensive patterns

Strategy: validation

Validate before calling

const SAFE_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
const DISALLOWED = new Set(['__proto__', 'constructor', 'prototype']);
function assertSafeMetadataKeys(metadata: Record<string, unknown> | undefined): void {
  for (const key of Object.keys(metadata ?? {})) {
    if (DISALLOWED.has(key) || !SAFE_KEY.test(key)) {
      throw new Error(`Invalid metadata key: "${key}"`);
    }
  }
}
assertSafeMetadataKeys(filter.metadata);

Type guard

function hasSafeMetadataKeys(m: Record<string, unknown>): boolean {
  const bad = new Set(['__proto__', 'constructor', 'prototype']);
  return Object.keys(m).every(k => !bad.has(k) && /^[A-Za-z_][A-Za-z0-9_]*$/.test(k));
}

Try / catch

try {
  const threads = await storage.listThreads({ metadata: filter.metadata });
} catch (e) {
  if (String((e as Error).message).startsWith('Invalid metadata key')) {
    throw new BadRequestError('metadata filter keys must be alphanumeric/underscore and not reserved');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `listThreads({ metadata: { 'my-key': ... } })` (hyphens/dots/spaces fail the pattern) or `{ '__proto__': ..., 'constructor': ... }` (disallowed keys) with `metadata` filters on thread listing.

Common situations: 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.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/9a1f724317e804a9. Report an issue: GitHub.