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

  1. Ensure the request context (organizationId, resourceId) and threadId used now match those under which the pin was created.
  2. 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.
  3. 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

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


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