{"record":{"id":"f393709a7e3e74e2","repo":"mastra-ai/mastra","slug":"learner-did-not-acknowledge-a-valid-reviewed-recor","errorCode":null,"errorMessage":"Learner did not acknowledge a valid reviewed record cursor.","messagePattern":"Learner did not acknowledge a valid reviewed record cursor\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/memory/src/processors/observational-memory/subconscious/learn.ts","lineNumber":193,"sourceCode":"        context,\n        scope,\n        worklist.records,\n        config,\n        subconscious,\n        options?.omModel,\n      );\n      const result = await agent.generate(\n        `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)}`,\n        {\n          requestContext: context.requestContext,\n          abortSignal: context.abortSignal,\n          maxSteps: config.maxSteps,\n          memory: { thread: `subconscious:${context.parentThreadId}:learn`, resource: context.resourceId },\n        },\n      );\n      const acknowledgedId = result.text.match(/<learning-complete\\s+through=[\"']([^\"']+)[\"']\\s*\\/>/i)?.[1];\n      if (!acknowledgedId || !worklist.records.some(record => record.id === acknowledgedId)) {\n        throw new Error('Learner did not acknowledge a valid reviewed record cursor.');\n      }\n      await store.advanceCurationCursor({\n        sourceThreadId: context.parentThreadId,\n        agent: LEARN_AGENT,\n        lastKnowledgeId: acknowledgedId,\n      });\n    } catch (error) {\n      const message = `learn: ${error instanceof Error ? error.message : String(error)}`;\n      await context.writer?.custom({ type: 'data-subconscious-error', data: { agent: 'learn', error: message } });\n      if (store && scope) {\n        await publishSubconsciousActivity({\n          store,\n          scope,\n          recentUpdates: subconscious.activity === false ? 10 : subconscious.activity.recentUpdates,\n          sendStateSignal: context.sendStateSignal,\n          errors: [message],\n        });\n      } else {","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/processors/observational-memory/subconscious/learn.ts#L175-L211","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: model output lacks the tag\nresult.text // \"I reviewed 5 records.\"\n// Error: Learner did not acknowledge a valid reviewed record cursor.\n// after: ensure the model ends with the acknowledgement\n// agent instructions end with: Always finish with\n// <learning-complete through=\"<last reviewed record id>\" />","handlingStrategy":"retry","validationCode":"// before trusting a learn run, confirm the agent's text ends with the acknowledgement\nconst acknowledgedId = result.text.match(/<learning-complete\\s+through=[\"']([^\"']+)[\"']\\s*\\/>/i)?.[1];\nconst valid = !!acknowledgedId && worklist.records.some(r => r.id === acknowledgedId);","typeGuard":"function hasValidAcknowledgement(text, records) {\n  const id = text.match(/<learning-complete\\s+through=[\"']([^\"']+)[\"']\\s*\\/>/i)?.[1];\n  return typeof id === 'string' && records.some(r => r.id === id);\n}","tryCatchPattern":"try {\n  await learn(memory, context);\n} catch (err) {\n  if (err.message.includes('did not acknowledge')) {\n    // safe to retry: cursor was not advanced; consider higher maxSteps or a stronger model\n  } else throw err;\n}","preventionTips":["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"],"tags":["llm-output","observational-memory","cursor-validation"],"backgroundTag":"llm-acknowledgment-tag-missing","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}