{"record":{"id":"2aedd98a03dc04f4","repo":"mastra-ai/mastra","slug":"rule-metadata-must-be-an-object","errorCode":null,"errorMessage":"Rule metadata must be an object.","messagePattern":"Rule metadata must be an object\\.","errorType":"validation","errorClass":"FactoryRuleValidationError","httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/rules/validation.ts","lineNumber":135,"sourceCode":"      throw new FactoryRuleValidationError('Rule metadata contains too many fields.');\n    }\n    const sanitized: Record<string, FactoryRuleJsonValue> = {};\n    for (const [key, entry] of entries) {\n      const normalizedKey = boundedString(key, 'Rule metadata key', 128, IDENTIFIER_RE);\n      sanitized[normalizedKey] = SENSITIVE_KEY_RE.test(normalizedKey)\n        ? '[REDACTED]'\n        : normalizeFactoryRuleJsonValue(entry, depth + 1, seen);\n    }\n    return sanitized;\n  } finally {\n    seen.delete(value as object);\n  }\n}\n\nfunction sanitizeMetadata(value: unknown): Record<string, FactoryRuleJsonValue> | undefined {\n  if (value === undefined) return undefined;\n  const sanitized = normalizeFactoryRuleJsonValue(value);\n  if (!isPlainObject(sanitized)) throw new FactoryRuleValidationError('Rule metadata must be an object.');\n  if (JSON.stringify(sanitized).length > MAX_METADATA_JSON_LENGTH) {\n    throw new FactoryRuleValidationError('Rule metadata is too large.');\n  }\n  return sanitized;\n}\n\nfunction validateBoardRules(rules: unknown, label: string): asserts rules is FactoryBoardRules {\n  if (!isPlainObject(rules)) throw new FactoryRuleValidationError(`${label} must be an object.`);\n  for (const [stage, sources] of Object.entries(rules)) {\n    enumValue(stage, FACTORY_RULE_STAGES, `${label} stage`);\n    if (!isPlainObject(sources)) throw new FactoryRuleValidationError(`${label}.${stage} must be an object.`);\n    for (const [source, leaf] of Object.entries(sources)) {\n      enumValue(source, FACTORY_RULE_SOURCES, `${label}.${stage} source`);\n      if (!isPlainObject(leaf)) throw new FactoryRuleValidationError(`${label}.${stage}.${source} must be an object.`);\n      assertExactKeys(leaf, ['onEnter', 'onExit'], `${label}.${stage}.${source}`);\n      for (const handler of Object.values(leaf)) {\n        if (handler !== undefined && typeof handler !== 'function') {\n          throw new FactoryRuleValidationError(`${label}.${stage}.${source} handlers must be functions.`);","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/rules/validation.ts#L117-L153","documentation":"The metadata field of an upsertLinkedWorkItem decision must normalize to a plain object of JSON values (or be omitted). If the top-level metadata value is an array, string, number, or boolean, sanitizeMetadata throws because downstream consumers expect a Record<string, FactoryRuleJsonValue>. Additionally the serialized metadata must stay under 16,384 characters ('Rule metadata is too large').","triggerScenarios":"Passing metadata as an array (e.g. metadata: ['a','b']), a bare string, or a number instead of an object; or passing an object whose JSON.stringify exceeds 16 KiB after sanitization/redaction.","commonSituations":"Confusing metadata (key-value record) with a payload/list field, storing a whole report blob in metadata and hitting the 16 KiB cap, LLM-generated decisions putting a list in metadata.","solutions":["Wrap the value in an object: metadata: { items: [...] } instead of metadata: [...].","Omit the metadata field entirely if there is nothing to attach.","If JSON.stringify(value).length > 16384, summarize, truncate lists, or move bulk data out of metadata.","Validate the shape (plain object) and approximate serialized size before emitting the decision."],"exampleFix":"// before\nmetadata: ['build-log', 'coverage']\n// after\nmetadata: { labels: ['build-log', 'coverage'] }","handlingStrategy":"validation","validationCode":"const safeMetadata = (m: unknown): Record<string, unknown> | undefined => {\n  if (m === undefined) return undefined;\n  if (typeof m !== 'object' || m === null || Array.isArray(m)) throw new TypeError('metadata must be an object');\n  if (JSON.stringify(m).length > 16384) throw new RangeError('metadata exceeds 16 KiB');\n  return m as Record<string, unknown>;\n};","typeGuard":"const isMetadataShape = (v: unknown): v is Record<string, unknown> =>\n  typeof v === 'object' && v !== null && !Array.isArray(v) && Object.getPrototypeOf(v) === Object.prototype;","tryCatchPattern":"try {\n  emit({ type: 'upsertLinkedWorkItem', metadata, /* ... */ });\n} catch (e) {\n  if (e instanceof FactoryRuleValidationError && /metadata (must be an object|is too large)/.test(e.message)) {\n    emit({ type: 'upsertLinkedWorkItem', metadata: { summary: JSON.stringify(metadata).slice(0, 2000) } });\n  } else throw e;\n}","preventionTips":["Always pass metadata as a top-level object; wrap lists in a named key.","Omit the metadata key when there is nothing to attach.","Check JSON.stringify(metadata).length <= 16384 before emitting decisions.","Centralize metadata construction in one helper that enforces both shape and size."],"tags":["validation","json","shape-mismatch"],"backgroundTag":"schema-validation-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}