thedotmack/claude-mem · error

generation parse error: ${outcome.reason}

Error message

generation parse error: ${outcome.reason}

What it means

Thrown by ProviderObservationGenerator when the generation outcome has kind 'parse_error' — the model returned content that could not be parsed into observations (processGeneratedResponse/processSessionSummaryResponse). Before throwing, it calls markGenerationFailed with classification 'parse_error' and retryable:false, so the job is permanently marked failed rather than requeued. The outcome.reason is included in the message.

Source

Thrown at src/server/generation/ProviderObservationGenerator.ts:262

      apiKeyId: payload.api_key_id,
      actorId: payload.actor_id,
      sourceAdapter: payload.source_adapter,
      ...(this.options.workerId !== undefined ? { workerId: this.options.workerId } : {}),
    };
    const outcome: ProcessGeneratedResponseOutcome = fresh.sourceType === 'session_summary'
      ? await processSessionSummaryResponse(persistInput)
      : await processGeneratedResponse(persistInput);

    if (outcome.kind === 'parse_error') {
      await markGenerationFailed({
        pool: this.options.pool,
        job: fresh,
        reason: outcome.reason,
        classification: 'parse_error',
        retryable: false,
        ...(this.options.workerId !== undefined ? { workerId: this.options.workerId } : {}),
      });
      throw new Error(`generation parse error: ${outcome.reason}`);
    }

    logger.info('SYSTEM', 'generation completed', {
      correlationId,
      jobId: outcome.jobId,
      bullmqJobId: job.id ?? null,
      requestId: payloadRequestId,
      observationCount: outcome.observations.length,
      privateContentDetected: outcome.privateContentDetected,
    });

    return {
      jobId: outcome.jobId,
      status: 'completed',
      observationCount: outcome.observations.length,
    };
  }

View on GitHub (pinned to d768ba3643)

Solutions

  1. Inspect outcome.reason in the message and the stored failed-job record to see the exact parse failure and any raw model output captured.
  2. Raise maxOutputTokens for the provider so the response isn't truncated.
  3. Retry the job manually if the failure was transient (a one-off malformed response).
  4. If a model change caused it, revert to a model known to follow the output schema, or adjust the prompt/parser.
  5. Check the provider's raw response (enable debug logging) to confirm whether it is truncation vs. format drift.

Example fix

// before: default token cap truncates large session output
new ClaudeObservationProvider({ apiKey, model });
// after: raise the cap so JSON isn't cut mid-object
new ClaudeObservationProvider({ apiKey, model, maxOutputTokens: 8192 });
Defensive patterns

Strategy: try-catch

Validate before calling

// Before submitting the job, sanity-check token budget vs. session size.
function estimateMaxTokensForSession(obsCharCount: number): number {
  // rough: ~4 chars/token; ensure headroom for JSON envelope
  const needed = Math.ceil(obsCharCount / 4) + 1024;
  return Math.max(4096, Math.min(needed, 16384));
}

Try / catch

try {
  await generator.process(job);
} catch (error) {
  if (/generation parse error/i.test((error as Error).message)) {
    // job already marked failed (retryable:false). Inspect outcome.reason from
    // the stored failed-job record; optionally re-enqueue after raising
    // maxOutputTokens or switching models.
    await inspectFailedJob(job.id);
    return;
  }
  throw error;
}

Prevention

When it happens

Trigger: The LLM returned prose instead of the expected structured output; maxOutputTokens was too low and the JSON was truncated mid-object; the model emitted markdown fences or extra commentary around the JSON; a schema/prompt drift between the model and the parser; an unexpected provider response shape.

Common situations: Switching to a smaller/cheaper model that ignores output-format instructions. maxOutputTokens left at default (4096) for a large session that needs more. A provider A/B change in output formatting. A session summary path expecting a different envelope.

Understand the failure class

Related errors


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/aa13888af9876f9c. Report an issue: GitHub.