mastra-ai/mastra · error
Record is not a pin: ${recordId}
Error message
Record is not a pin: ${recordId} What it means
requirePin also verifies that the resolved record actually lives on the reserved pinned-knowledge node (record.node must equal the node id resolved for the configured scope). A record id that points at ordinary knowledge — e.g. a learned or curated record — is rejected with this error. This prevents callers from unpinning or mutating non-pin knowledge through the pin tools.
Source
Thrown at packages/memory/src/processors/observational-memory/subconscious/pinned.ts:179
defaultScope: expandKnowledgeScope(options.scope, options.defaultScope),
});
}
async function getStore(memory: PinnedMemory): Promise<KnowledgeStorage> {
const store = await memory.storage.getStore('knowledge');
if (!store) throw new Error('Pinned knowledge requires a configured knowledge storage domain.');
return store;
}
async function requirePin(
store: KnowledgeStorage,
recordId: string,
options: PinnedToolsOptions,
): Promise<KnowledgeRecord> {
const record = await store.getKnowledge({ id: recordId, includeDeleted: false });
if (!record) throw new Error(`Pin not found: ${recordId}`);
const nodeId = await resolvePinnedNodeId(store, options.scope);
if (!nodeId || record.node !== nodeId) throw new Error(`Record is not a pin: ${recordId}`);
if (!isKnowledgeScopeVisible(record.scope, options.scope)) throw new Error('Pin is outside the visible scope.');
return record;
}
/**
* Pin lifecycle tools. Pin appends a record on the reserved node; unpin soft-deletes it
* (auditable, restorable); edit is remove plus append because knowledge records are immutable,
* so an edited pin carries a new record id.
*/
export function createPinnedTools(
memory: PinnedMemory,
options: PinnedToolsOptions,
): Record<string, ToolAction<any, any, any>> {
return {
knowledge_pin: createTool({
id: 'knowledge_pin',
description:
'Pin knowledge that must stay in context every turn without being asked for. Pins cost context permanently; pin only what is unconditionally relevant.',View on GitHub (pinned to 75dd419e61)
Solutions
- Use only ids returned by the pin/list tool, not from general knowledge lookups
- Ensure the pinned tools' scope configuration matches the scope where the pins were created
- Look up the reserved pinned node and enumerate its records to get valid pin ids
- If the intent is to manage non-pin knowledge, use the general knowledge APIs, not pin tools
Example fix
// before: using a learned record id with the unpin tool
await unpinTool.execute({ recordId: learnedRecord.id });
// Error: Record is not a pin: kn_456
// after: use an id from the pin listing
const pins = await listPins(memory, { scope });
await unpinTool.execute({ recordId: pins[0].id }); Defensive patterns
Strategy: validation
Validate before calling
const record = await store.getKnowledge({ id: recordId, includeDeleted: false });
const nodeId = await resolvePinnedNodeId(store, scope);
if (!record || !nodeId || record.node !== nodeId) {
throw new Error(`${recordId} is not a pin; use ids returned by the pin listing.`);
} Type guard
function isPinOnReservedNode(record, pinnedNodeId) {
return !!record && !!pinnedNodeId && record.node === pinnedNodeId;
} Try / catch
try {
await pinTool.execute({ recordId });
} catch (err) {
if (err.message.startsWith('Record is not a pin')) {
// fetch a valid pin id from the pin list; do not retry with the same id
} else throw err;
} Prevention
- Only pass ids obtained from pin-listing tools
- Keep the pinned tools' scope identical to the scope used to create pins
- Separate pin ids from learned/curated knowledge ids in agent state
- When managing non-pin knowledge, use the general knowledge APIs
When it happens
Trigger: Passing a valid knowledge record id that belongs to a regular (non-reserved) node to a pin tool; calling pin tools with an id from the wrong scope so resolvePinnedNodeId resolves a different (or missing) reserved node.
Common situations: Agent grabbing an arbitrary knowledge id (from learned records) and attempting to unpin it; pin tools configured with a different scope than the one the pins were created under; mixing pinned and learner record ids in one client.
Related errors
- Cannot merge a knowledge node into itself
- Knowledge node is not a skill: ${normalizedName}
- Pin limit reached: the set holds at most ${options.maxPins}.
- Pin budget exceeded: the pin set is limited to ${options.max
- Pin not found: ${recordId}
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/09a7aba258c95d34.
Report an issue: GitHub.