{"record":{"id":"51ff22951313a96c","repo":"mastra-ai/mastra","slug":"multi-thread-observer-produced-degenerate-output-a","errorCode":null,"errorMessage":"Multi-thread observer produced degenerate output after retry. ${describeDegenerateOutput(result.text)}","messagePattern":"Multi-thread observer produced degenerate output after retry\\. (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/memory/src/processors/observational-memory/observer-runner.ts","lineNumber":543,"sourceCode":"      );\n    };\n\n    let result = await doGenerate();\n    let parsed = parseMultiThreadObserverOutput(result.text, activeExtractors);\n    let retriedDueToDegenerate = false;\n\n    if (parsed.degenerate) {\n      omDebug(\n        `[OM:callMultiThreadObserver] degenerate repetition detected, retrying once. ${describeDegenerateOutput(result.text, 2000)}`,\n      );\n      result = await doGenerate();\n      parsed = parseMultiThreadObserverOutput(result.text, activeExtractors);\n      retriedDueToDegenerate = true;\n      if (parsed.degenerate) {\n        omDebug(\n          `[OM:callMultiThreadObserver] degenerate repetition on retry, failing. ${describeDegenerateOutput(result.text, 2000)}`,\n        );\n        throw new Error(\n          `Multi-thread observer produced degenerate output after retry. ${describeDegenerateOutput(result.text)}`,\n        );\n      }\n    }\n\n    const structuredExtractionByThread = new Map<string, Awaited<ReturnType<typeof extractStructuredValues>>>();\n\n    const aggregatedExtractedValues = mergeExtractedValues(\n      ...Array.from(parsed.threads, ([threadId, threadResult]) =>\n        mergeExtractedValues(threadResult.extractedValues, structuredExtractionByThread.get(threadId)?.values),\n      ),\n    );\n    const aggregatedExtractionFailures = mergeExtractionFailures(\n      ...Array.from(parsed.threads, ([threadId, threadResult]) =>\n        mergeExtractionFailures(threadResult.extractionFailures, structuredExtractionByThread.get(threadId)?.failures),\n      ),\n    );\n    const aggregatedBuiltIns = getBuiltInExtractedValues(aggregatedExtractedValues);","sourceCodeStart":525,"sourceCodeEnd":561,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/processors/observational-memory/observer-runner.ts#L525-L561","documentation":"`ObserverRunner.callMultiThread` runs the multi-thread Observer variant that summarizes several threads at once. Its output is parsed by `parseMultiThreadObserverOutput`, which detects degenerate repetition; if the automatic single retry also yields degenerate output, this error is thrown (observer-runner.ts:543). It protects cross-thread memory from being overwritten by a model that fell into a repetition loop.","triggerScenarios":"Multi-thread observation is enabled (per-resource observation across multiple threads) and the observation model returns repetition-looped text for the combined multi-thread prompt; the built-in one-shot retry in `callMultiThread` also parses as degenerate, so the batch fails. More likely with small/quantized models given the larger, more complex multi-thread prompt.","commonSituations":"Running observation across many long threads with a local 1-3B model that loops on the large prompt; context-window overflow when batching multiple threads (truncated output triggers repetition); misconfigured routing thresholds selecting a weak model for large inputs; provider degradation returning repetitive filler.","solutions":["Switch to a stronger observation model via `observationModel`, or adjust `routingThresholds`/`routingStrategy` so large multi-thread batches route to a capable model.","Reduce batch size: observe threads more frequently or lower per-cycle thread/token limits so the multi-thread prompt fits comfortably in context.","Add repetition-curbing `modelSettings` (frequencyPenalty/repetitionPenalty, moderate temperature) to the observation config.","Inspect the `describeDegenerateOutput` snippet in the message to identify the looping content, and check whether a specific thread's content triggers it (move that thread to single-thread observation or exclude it)."],"exampleFix":"// before\nnew ObservationalMemory({\n  observationModel: 'ollama/qwen2.5:0.5b',\n  perResource: { enabled: true }, // multi-thread batches overflow a small model\n});\n\n// after\nnew ObservationalMemory({\n  observationModel: 'openai/gpt-4o-mini',\n  perResource: { enabled: true },\n  modelSettings: { frequencyPenalty: 0.5 },\n});","handlingStrategy":"fallback","validationCode":"// before enabling multi-thread observation, ensure the model can handle the combined prompt\nconst totalTokens = threads.reduce((n, t) => n + countTokens(t.messages), 0);\nif (totalTokens > modelMaxInputTokens * 0.6) {\n  throw new Error('multi-thread batch too large for observation model; reduce batch or use stronger model');\n}","typeGuard":"function looksDegenerate(text: string): boolean {\n  const chunks = text.split(/\\n{2,}/).map(c => c.trim()).filter(Boolean);\n  if (chunks.length < 3) return false;\n  return new Set(chunks).size / chunks.length < 0.5;\n}","tryCatchPattern":"try {\n  await memory.process(messages, { abortSignal: signal });\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Multi-thread observer produced degenerate output')) {\n    logger.warn('multi-thread observer looping; falling back to per-thread observation');\n    await processPerThread(messages, { abortSignal: signal });\n    return;\n  }\n  throw err;\n}","preventionTips":["Route large multi-thread batches to a strong model via routingStrategy/routingThresholds.","Limit threads-per-batch and total input tokens so the prompt fits with headroom.","Add repetition penalties to observer modelSettings.","Pin down which thread triggers looping by logging batch contents when this error occurs."],"tags":["llm-output","degenerate-repetition","multi-thread","observational-memory"],"backgroundTag":"llm-degenerate-repetition","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}