mastra-ai/mastra · error · Error

The learner may record at most one skill per reflection.

Error message

The learner may record at most one skill per reflection.

What it means

The learner records at most one skill per reflection. Once a skill name is recorded in the reflection state, any subsequent record-skill call with a different (case-insensitive) name throws this error. This constrains skill extraction to one procedure per reflection cycle, keeping the learned-skill log auditable and avoiding runaway skill creation.

Source

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

        procedure: { type: 'string', minLength: 1 },
        sourceRecordIds: { type: 'array', items: { type: 'string', minLength: 1 }, minItems: 2, uniqueItems: true },
      },
      required: ['name', 'procedure', 'sourceRecordIds'],
      additionalProperties: false,
    } satisfies JSONSchema7,
    execute: async raw => {
      const value = raw as { name: string; procedure: string; sourceRecordIds: string[] };
      const sourceIds = [...new Set(value.sourceRecordIds)];
      const pending = new Map(input.pendingRecords.map(record => [record.id, record]));
      if (sourceIds.length < 2 || sourceIds.some(id => !pending.has(id))) {
        throw new Error('Skill evidence requires at least two distinct records from the pending learner worklist.');
      }
      const normalizedName = value.name.trim();
      if (
        input.state.recordedName &&
        input.state.recordedName.toLocaleLowerCase() !== normalizedName.toLocaleLowerCase()
      ) {
        throw new Error('The learner may record at most one skill per reflection.');
      }
      input.state.recordedName = normalizedName;
      const nodeScope = expandKnowledgeScope(input.scope, input.defaultScope);
      let node = await input.store.resolveNode({ name: normalizedName, scope: input.scope });
      if (node && node.kind !== 'skill') throw new Error(`Knowledge node is not a skill: ${normalizedName}`);
      node ??= await input.store.createNode({ name: normalizedName, kind: 'skill', scope: nodeScope });
      const evidence = [];
      for (const sourceId of sourceIds) {
        const id = evidenceRecordId(sourceId, normalizedName);
        const existing = await input.store.getKnowledge({ id });
        if (existing) {
          evidence.push(existing);
          continue;
        }
        const source = pending.get(sourceId)!;
        try {
          evidence.push(
            await input.store.appendKnowledge({

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Record only one skill per reflection and defer additional skills to the next reflection cycle
  2. Reuse the same skill name if updating/extending the skill already recorded this reflection
  3. Check input.state.recordedName before calling and skip or no-op if a skill was already recorded

Example fix

// before
await tool.execute({ name: 'skill-a', procedure, sourceRecordIds });
await tool.execute({ name: 'skill-b', procedure2, sourceRecordIds2 }); // throws
// after
await tool.execute({ name: 'skill-a', procedure, sourceRecordIds });
// handle skill-b in the next reflection cycle
Defensive patterns

Strategy: try-catch

Validate before calling

if (state.recordedName && state.recordedName.toLowerCase() !== name.trim().toLowerCase()) {
  throw new Error('A skill was already recorded this reflection; defer to next cycle');
}

Try / catch

try {
  await tool.execute({ name, procedure, sourceRecordIds });
} catch (e) {
  if (e instanceof Error && e.message.includes('at most one skill per reflection')) {
    // skip; schedule this skill for the next reflection
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the record-skill tool twice within the same reflection with different skill names; the reflection loop retrying with a renamed skill after the first call already succeeded.

Common situations: LLM deciding to capture two procedures in one reflection; retry logic that rephrases the skill name; concurrent tool invocations sharing the same state object.

Related errors


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