{"record":{"id":"40130a5d94b57d6e","repo":"mastra-ai/mastra","slug":"reflector-produced-empty-output-after-attemptnum","errorCode":null,"errorMessage":"Reflector produced empty output after ${attemptNumber} attempt(s)${parsed.degenerate ? ' (degenerate repetition)' : ''} — refusing to commit an empty reflection over ${originalTokens} observation tokens","messagePattern":"Reflector produced empty output after (.+?) attempt\\(s\\)(.+?) — refusing to commit an empty reflection over (.+?) observation tokens","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/memory/src/processors/observational-memory/reflector-runner.ts","lineNumber":572,"sourceCode":"          config: this.getObservationMarkerConfig(),\n        });\n        streamContext.startedAt = startMarker.data.startedAt;\n        // Stream OM lifecycle markers as transient so the OutputWriter does not persist standalone data-only messages; OM persists the durable marker explicitly.\n        await streamContext.writer.custom({ ...startMarker, transient: true }).catch(() => {});\n        await this.persistMarkerToStorage(startMarker, streamContext.threadId, streamContext.resourceId);\n      }\n\n      currentLevel = Math.min(currentLevel + 1, maxLevel) as CompressionLevel;\n    }\n\n    // A reflection of non-empty observations must never come back empty: the\n    // caller commits the result as the new activeObservations (sync path) or\n    // as the bufferedReflection replacing the reflected slice (buffered path),\n    // so returning '' here silently wipes memory. Empty output only happens\n    // when every ladder attempt was degenerate (parseReflectorOutput discards\n    // degenerate text) or the model returned nothing — both are failures.\n    if (observations.trim().length > 0 && parsed.observations.trim().length === 0) {\n      throw new Error(\n        `Reflector produced empty output after ${attemptNumber} attempt(s)${parsed.degenerate ? ' (degenerate repetition)' : ''} — refusing to commit an empty reflection over ${originalTokens} observation tokens`,\n      );\n    }\n\n    const structuredExtraction = await extractStructuredValues({\n      agent,\n      source: 'reflector',\n      extractors: activeExtractors,\n      memory: temporaryMemory?.options,\n      priorExtractedValues,\n      requestContext: internalRequestContext,\n      observabilityContext,\n      abortSignal,\n    });\n    const parsedExtractedValues = mergeExtractedValues(parsed.extractedValues, structuredExtraction.values);\n    const parsedExtractionFailures = mergeExtractionFailures(parsed.extractionFailures, structuredExtraction.failures);\n    const hookedValues = await applyExtractorHooks({\n      source: 'reflector',","sourceCodeStart":554,"sourceCodeEnd":590,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/processors/observational-memory/reflector-runner.ts#L554-L590","documentation":"After exhausting the reflection ladder, if the model returned empty or degenerate (repetitive) observations for non-empty input, the reflector throws instead of committing. Committing empty output would silently wipe the active observations slice, so this is a hard safety guard.","triggerScenarios":"All retry/ladder attempts of the reflector agent produced whitespace-only or degenerate repetition output while the original observation tokens were non-empty (observations.trim().length > 0 but parsed.observations empty).","commonSituations":"Misconfigured/low-quality model for the reflector; model hitting token limits and returning nothing; model loops producing degenerate repetition; reflector agent prompt overridden incorrectly.","solutions":["Use a stronger model for the reflector (check reflector model config in ObservationalMemory options)","Review the reflector prompt/instructions for token limits or constraints causing empty replies","Reduce the observation batch size so the reflection fits within the model's output budget","Retry the reflection — transient model failures may resolve on a later call"],"exampleFix":"// before\nnew ObservationalMemory({ reflector: { model: 'tiny-model' } })\n// after\nnew ObservationalMemory({ reflector: { model: 'openai/gpt-4o' } })","handlingStrategy":"retry","validationCode":"const obs = observations.trim(); if (!obs) return; // nothing to reflect","typeGuard":"const isEmptyReflection = (out: string) => out.trim().length === 0;","tryCatchPattern":"try { result = await reflect(...) } catch (e) { if (String(e).includes('empty output')) { logAndRetryOrSkip(e); } else throw e; }","preventionTips":["Configure a capable model for the reflector","Keep reflection input within model output budget","Monitor for degenerate repetition and alert","Retry reflections on transient failures"],"tags":["llm","reflection","empty-output","memory"],"backgroundTag":"empty-model-response","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}