mastra-ai/mastra · error · Error

Skill evidence requires at least two distinct records from t

Error message

Skill evidence requires at least two distinct records from the pending learner worklist.

What it means

The learner's record-skill tool demands evidence: the cited sourceRecordIds must contain at least two distinct IDs, and every ID must belong to the pending learner worklist for the current reflection. This prevents the learner from minting skills (procedural knowledge) from a single observation or from records it was never shown.

Source

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

    id: 'knowledge_record_skill',
    description:
      'Create or update one reusable skill using at least two distinct pending knowledge records. Evidence writes are idempotent across retries.',
    inputSchema: {
      type: 'object',
      properties: {
        name: { type: 'string', minLength: 1 },
        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) {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Pick at least two distinct record IDs from the pending worklist handed to the learner in the current reflection
  2. Dedupe and verify each ID against the pending list before invoking the tool
  3. Re-run the reflection if the worklist has changed so the learner gets fresh record IDs

Example fix

// before
await tool.execute({ name, procedure, sourceRecordIds: [id] });
// after
const ids = [...new Set(pending.map(r => r.id))].slice(0, 2);
if (ids.length < 2) throw new Error('Need >=2 distinct pending records');
await tool.execute({ name, procedure, sourceRecordIds: ids });
Defensive patterns

Strategy: validation

Validate before calling

const ids = [...new Set(sourceRecordIds)];
const pendingIds = new Set(pendingRecords.map(r => r.id));
if (ids.length < 2 || !ids.every(id => pendingIds.has(id))) {
  throw new Error('Need >=2 distinct records from the pending worklist');
}

Type guard

function hasEnoughEvidence(ids: string[], pending: { id: string }[]): ids is [string, string, ...string[]] {
  const set = new Set(pending.map(p => p.id));
  const distinct = ids.filter(id => set.has(id));
  return distinct.length >= 2;
}

Try / catch

try {
  await tool.execute({ name, procedure, sourceRecordIds });
} catch (e) {
  if (e instanceof Error && e.message.includes('at least two distinct records')) {
    // re-pick evidence from the current pending worklist
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the record-skill tool with fewer than two unique record IDs, duplicate IDs that dedupe to one, or IDs not present in the pendingRecords worklist passed to createLearnerRecordSkillTool (e.g. records from another thread or already-consumed records).

Common situations: LLM citing record IDs it hallucinated or saw in earlier reflections; duplicates in the ID array collapsing below two after dedupe; worklist rotated between planning and tool execution.

Related errors


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