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
- Increase config.maxSteps so the learner has turns left to emit the acknowledgement
- 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
- Ensure custom config.instructions do not override DEFAULT_INSTRUCTIONS acknowledgement requirements
- Use a stronger model for the learner; retry the learn step (cursor was not advanced, work is re-processed)
- 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
- Set config.maxSteps generously so the agent has budget to acknowledge
- Never remove the acknowledgement instruction when overriding config.instructions
- Use a model reliable at exact output formats for the learner
- Retry is safe because the cursor only advances on valid acknowledgement
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
- Extractor "${extractor.slug}" output did not match its schem
- ${EXTRACTED_VALUES_TAG} must contain a JSON object.
- Observer produced degenerate output after retry. ${describeD
- Multi-thread observer produced degenerate output after retry
- Curator did not acknowledge a valid processed KnowledgeRecor
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/f393709a7e3e74e2.
Report an issue: GitHub.