mastra-ai/mastra · error
Pin is outside the visible scope.
Error message
Pin is outside the visible scope.
What it means
Thrown by `requirePin` in packages/memory/src/processors/observational-memory/subconscious/pinned.ts:180 when a pin record exists on the reserved pin node, but the record's knowledge scope is not visible within the caller's currently configured scope (`options.scope`). The library enforces scope isolation: a pin recorded under a different (e.g. broader or other-thread) scope cannot be operated on from the current scope context.
Source
Thrown at packages/memory/src/processors/observational-memory/subconscious/pinned.ts:180
});
}
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.',
inputSchema: {View on GitHub (pinned to 75dd419e61)
Solutions
- Ensure the request context (organizationId, resourceId) and threadId used now match those under which the pin was created.
- Check the pin's record.scope in the knowledge store before invoking the tool and only operate on pins whose scope is visible from your active scope.
- If the pin must move, remove it from the old scope and re-append under the new scope (records are immutable; edit = remove + append).
Example fix
// before (scope mismatch, e.g. different thread)
await pinnedTool({ recordId, scope: ['org:acme','resource:bot','thread:thread-b'] });
// after (use the scope the pin was created under)
const record = await store.getKnowledge({ id: recordId, includeDeleted: false });
await pinnedTool({ recordId, scope: record.scope }); Defensive patterns
Strategy: validation
Validate before calling
const record = await store.getKnowledge({ id: recordId, includeDeleted: false });
if (!record) throw new Error(`Pin not found: ${recordId}`);
if (!record.scope.every((key, i) => options.scope[i] === key)) {
throw new Error(`Pin ${recordId} scope ${record.scope} is not visible from ${options.scope}`);
} Type guard
function isPinVisibleInScope(record, scope) {
return Array.isArray(record?.scope) && record.scope.every((key, i) => scope[i] === key);
} Try / catch
try {
await pinnedTool({ recordId, scope: currentScope });
} catch (e) {
if (e.message === 'Pin is outside the visible scope.') {
// fall back to the pin's own scope or surface a user-facing 'not found' error
} else throw e;
} Prevention
- Always derive operation scope from the same requestContext used when the pin was created.
- Store scope metadata alongside your own references to pin recordIds.
- Write tests covering cross-thread/cross-org pin access.
When it happens
Trigger: Calling a pin lifecycle tool (via `record`, which calls `requirePin`) with a recordId whose record passes the `record.node === nodeId` check but whose `record.scope` fails `isKnowledgeScopeVisible(record.scope, options.scope)` — e.g. unpinning/editing a pin created under `org:X,resource:Y,thread:T1` while the subconscious now resolves scope for `thread:T2`, or the requestContext's organizationId/resourceId changed since the pin was created.
Common situations: Multi-tenant apps where organizationId or resourceId in requestContext changes between pin creation and usage; thread ID renames/migrations; invoking pinned-record tools from a different agent thread than the one that created the pin; restoring soft-deleted records across scope boundaries.
Related errors
- AGENT_NETWORK_OBSERVATIONAL_MEMORY_UNSUPPORTED
- Observational memory record not found: ${currentRecord.id}
- No buffered reflection to swap
- Observational memory is enabled but the installed version of
- Observational memory async buffering is enabled by default b
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/8d39d382e77d35ea.
Report an issue: GitHub.