mastra-ai/mastra · error · FactoryRuleValidationError

Rule metadata contains too many entries.

Error message

Rule metadata contains too many entries.

What it means

Each array inside rule metadata is capped at MAX_JSON_COLLECTION_SIZE (100) entries. Arrays longer than that are rejected so a single metadata blob cannot grow unbounded; the sanitized result must stay compact since total metadata is also capped at 16 KiB of JSON.

Source

Thrown at mastracode/factory/src/rules/validation.ts:110

export function normalizeFactoryRuleJsonValue(
  value: unknown,
  depth = 0,
  seen = new Set<object>(),
): FactoryRuleJsonValue {
  if (value === null || typeof value === 'boolean' || typeof value === 'string') return value;
  if (typeof value === 'number') {
    if (!Number.isFinite(value)) throw new FactoryRuleValidationError('Rule metadata must contain finite numbers.');
    return value;
  }
  if (depth >= MAX_JSON_DEPTH || (typeof value !== 'object' && !Array.isArray(value))) {
    throw new FactoryRuleValidationError('Rule metadata is not bounded JSON.');
  }
  if (seen.has(value as object)) throw new FactoryRuleValidationError('Rule metadata must not contain cycles.');
  seen.add(value as object);
  try {
    if (Array.isArray(value)) {
      if (value.length > MAX_JSON_COLLECTION_SIZE) {
        throw new FactoryRuleValidationError('Rule metadata contains too many entries.');
      }
      return value.map(entry => normalizeFactoryRuleJsonValue(entry, depth + 1, seen));
    }
    if (!isPlainObject(value)) throw new FactoryRuleValidationError('Rule metadata must use plain objects.');
    const entries = Object.entries(value);
    if (entries.length > MAX_JSON_COLLECTION_SIZE) {
      throw new FactoryRuleValidationError('Rule metadata contains too many fields.');
    }
    const sanitized: Record<string, FactoryRuleJsonValue> = {};
    for (const [key, entry] of entries) {
      const normalizedKey = boundedString(key, 'Rule metadata key', 128, IDENTIFIER_RE);
      sanitized[normalizedKey] = SENSITIVE_KEY_RE.test(normalizedKey)
        ? '[REDACTED]'
        : normalizeFactoryRuleJsonValue(entry, depth + 1, seen);
    }
    return sanitized;
  } finally {
    seen.delete(value as object);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Slice the array to 100 items: arr.slice(0, 100).
  2. Store a summary instead: counts, first N items, or a compact hash/digest string.
  3. Move bulk data to an external store (file, artifact) and put a reference URL in metadata.
  4. Chunk the data across multiple work-item updates if all entries are required.

Example fix

// before
metadata: { files: allChangedFiles } // 500 entries
// after
metadata: { files: allChangedFiles.slice(0, 100), totalFiles: allChangedFiles.length }
Defensive patterns

Strategy: validation

Validate before calling

const capArray = (a: unknown[], max = 100) => a.length > max ? a.slice(0, max) : a;
metadata = { ...metadata, files: capArray(files), totalFiles: files.length };

Type guard

const fitsCollectionCap = (v: unknown): boolean =>
  !Array.isArray(v) || v.length <= 100;

Try / catch

try {
  emit({ type: 'upsertLinkedWorkItem', metadata });
} catch (e) {
  if (e instanceof FactoryRuleValidationError && e.message.includes('too many entries')) {
    emit({ type: 'upsertLinkedWorkItem', metadata: truncateArrays(metadata, 100) });
  } else throw e;
}

Prevention

When it happens

Trigger: Passing metadata with an array of more than 100 items, e.g. a list of all changed files, all test results, or all log lines collected by the rule into an upsertLinkedWorkItem decision's metadata field.

Common situations: Logging large batch results (files, commits, test cases) into metadata, aggregating events over a long run, or forwarding an API response array directly into metadata.

Related errors


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