mastra-ai/mastra · error · Error

${EXTRACTED_VALUES_TAG} must contain a JSON object.

Error message

${EXTRACTED_VALUES_TAG} must contain a JSON object.

What it means

When the observer/reflector output contains an `<extracted-values>` block, Observational Memory parses its contents as a JSON object keyed by extractor slug. `parseExtractedValuesObject` throws this error when the tag's content is valid JSON but not an object — e.g. an array, string, number, or `null`. The error is recorded as a failure under the 'extracted-values' slug rather than crashing the stream.

Source

Thrown at packages/memory/src/processors/observational-memory/extractor.ts:292

    failures.push(parsed.error.message);
  }
  throw new Error(`Extractor "${extractor.slug}" output did not match its schema: ${failures[0] ?? 'invalid value'}`);
}

export interface ParsedExtractedValues {
  values: Record<string, unknown>;
  failures: Array<{ slug: string; error: string }>;
}

function parseExtractedValuesObject(raw: string): Record<string, unknown> | undefined {
  const trimmed = raw.trim();
  if (!trimmed || trimmed.startsWith('Write only the extracted values JSON object here.')) {
    return undefined;
  }

  const parsed = JSON.parse(trimmed) as unknown;
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
    throw new Error(`${EXTRACTED_VALUES_TAG} must contain a JSON object.`);
  }
  return parsed as Record<string, unknown>;
}

export function parseExtractedValues(output: string, extractors: readonly Extractor<any>[]): ParsedExtractedValues {
  const values: Record<string, unknown> = {};
  const failures: Array<{ slug: string; error: string }> = [];
  const inlineExtractors = extractors.filter(extractor => extractor.mode === 'inline');

  const regex = new RegExp(`<${EXTRACTED_VALUES_TAG}>([\\s\\S]*?)<\\/${EXTRACTED_VALUES_TAG}>`, 'gi');
  const matches = [...output.matchAll(regex)];
  for (const match of matches) {
    try {
      const extractedValues = parseExtractedValuesObject(match[1] ?? '');
      if (!extractedValues) {
        continue;
      }

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Strengthen extractor instructions to say 'Output a single JSON object whose keys are the extractor tags and whose values are the extracted values.'
  2. Post-process the model output before parsing (or rely on the recorded failure and retry the extraction pass).
  3. Use a stronger model for the reflector/observer if arrays persist.
  4. Catch failures via the ParsedExtractedValues.failures array and inject corrective feedback on retry.

Example fix

// model output before
<extracted-values>[{"user-goals":"run a marathon"}]</extracted-values>
// model output after
<extracted-values>{"user-goals":"run a marathon"}</extracted-values>
Defensive patterns

Strategy: try-catch

Validate before calling

function isJsonObjectString(s) {
  try { const v = JSON.parse(s); return v !== null && typeof v === 'object' && !Array.isArray(v); }
  catch { return false; }
}

Type guard

function isRecord(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }

Try / catch

const { values, failures } = parseExtractedValues(output, extractors);
const shapeFailures = failures.filter(f => f.slug === 'extracted-values');
if (shapeFailures.length) {
  logger.warn('extracted-values block was not a JSON object; retrying extraction', { error: shapeFailures[0].error });
}

Prevention

When it happens

Trigger: Model emits a JSON array `[...]` inside <extracted-values> instead of an object; model emits a bare string or number; model wraps a JSON array of {slug,value} pairs; JSON.stringify of an array being echoed back by the model.

Common situations: Models that interpret 'extracted values' as a list rather than a map; prompts with multiple extractors leading the model to output an array of records; weak models ignoring the object-shape instruction.

Related errors


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