mastra-ai/mastra · error

Learner did not acknowledge a valid reviewed record cursor.

Error message

Learner did not acknowledge a valid reviewed record cursor.

What it means

After running the learner agent over the worklist, the handler parses the model's final text for an acknowledgement tag `<learning-complete through="<recordId>"/>`. If the tag is missing, malformed, or names a record id not present in the current worklist, the handler throws instead of advancing the curation cursor. This guarantees the durable cursor only moves when the model has genuinely confirmed processing through a valid record, preventing silent skipping of knowledge records.

Source

Thrown at packages/memory/src/processors/observational-memory/subconscious/learn.ts:193

        context,
        scope,
        worklist.records,
        config,
        subconscious,
        options?.omModel,
      );
      const result = await agent.generate(
        `Parent thread: ${context.parentThreadId}\nCurrent time: ${new Date().toISOString()}\nWorklist truncated: ${worklist.hasMore}\n\nFull pre-reflection observations:\n${context.observations}\n\nPending knowledge records:\n${JSON.stringify(worklist.records)}`,
        {
          requestContext: context.requestContext,
          abortSignal: context.abortSignal,
          maxSteps: config.maxSteps,
          memory: { thread: `subconscious:${context.parentThreadId}:learn`, resource: context.resourceId },
        },
      );
      const acknowledgedId = result.text.match(/<learning-complete\s+through=["']([^"']+)["']\s*\/>/i)?.[1];
      if (!acknowledgedId || !worklist.records.some(record => record.id === acknowledgedId)) {
        throw new Error('Learner did not acknowledge a valid reviewed record cursor.');
      }
      await store.advanceCurationCursor({
        sourceThreadId: context.parentThreadId,
        agent: LEARN_AGENT,
        lastKnowledgeId: acknowledgedId,
      });
    } catch (error) {
      const message = `learn: ${error instanceof Error ? error.message : String(error)}`;
      await context.writer?.custom({ type: 'data-subconscious-error', data: { agent: 'learn', error: message } });
      if (store && scope) {
        await publishSubconsciousActivity({
          store,
          scope,
          recentUpdates: subconscious.activity === false ? 10 : subconscious.activity.recentUpdates,
          sendStateSignal: context.sendStateSignal,
          errors: [message],
        });
      } else {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Increase config.maxSteps so the learner has turns left to emit the acknowledgement
  2. Check the model's result.text (logs) to see whether the tag was emitted with a wrong/missing id; re-run after fixing the id source
  3. Ensure custom config.instructions do not override DEFAULT_INSTRUCTIONS acknowledgement requirements
  4. Use a stronger model for the learner; retry the learn step (cursor was not advanced, work is re-processed)
  5. Verify worklist records have stable ids and the parent thread is the same one the cursor was created for

Example fix

// before: model output lacks the tag
result.text // "I reviewed 5 records."
// Error: Learner did not acknowledge a valid reviewed record cursor.
// after: ensure the model ends with the acknowledgement
// agent instructions end with: Always finish with
// <learning-complete through="<last reviewed record id>" />
Defensive patterns

Strategy: retry

Validate before calling

// before trusting a learn run, confirm the agent's text ends with the acknowledgement
const acknowledgedId = result.text.match(/<learning-complete\s+through=["']([^"']+)["']\s*\/>/i)?.[1];
const valid = !!acknowledgedId && worklist.records.some(r => r.id === acknowledgedId);

Type guard

function hasValidAcknowledgement(text, records) {
  const id = text.match(/<learning-complete\s+through=["']([^"']+)["']\s*\/>/i)?.[1];
  return typeof id === 'string' && records.some(r => r.id === id);
}

Try / catch

try {
  await learn(memory, context);
} catch (err) {
  if (err.message.includes('did not acknowledge')) {
    // safe to retry: cursor was not advanced; consider higher maxSteps or a stronger model
  } else throw err;
}

Prevention

When it happens

Trigger: Learner agent finishes (maxSteps exhausted or stops early) without emitting the learning-complete tag; emits a tag with an id that is not one of worklist.records ids (hallucinated or from a stale worklist); tag written with wrong syntax so the regex fails.

Common situations: Weak/small model ignoring the output-format instruction; maxSteps too low so the agent runs out of turns before acknowledging; prompt customization (config.instructions) removing or overriding the acknowledgement requirement; model echoes the tag with surrounding markdown that still matches but with a stale id.

Related errors


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