{"record":{"id":"a73550b56a5e9197","repo":"mastra-ai/mastra","slug":"pin-budget-exceeded-the-pin-set-is-limited-to-o","errorCode":null,"errorMessage":"Pin budget exceeded: the pin set is limited to ${options.maxCharacters} characters in total.","messagePattern":"Pin budget exceeded: the pin set is limited to (.+?) characters in total\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/memory/src/processors/observational-memory/subconscious/pinned.ts","lineNumber":118,"sourceCode":"  return { nodeId, pins };\n}\n\nfunction totalCharacters(pins: KnowledgeRecord[]): number {\n  return pins.reduce((sum, pin) => sum + pin.text.length, 0);\n}\n\nfunction assertBudget(\n  options: PinnedToolsOptions,\n  pins: KnowledgeRecord[],\n  incomingText: string,\n  replacing?: KnowledgeRecord,\n): void {\n  const kept = replacing ? pins.filter(pin => pin.id !== replacing.id) : pins;\n  if (!replacing && kept.length >= options.maxPins) {\n    throw new Error(`Pin limit reached: the set holds at most ${options.maxPins}. Unpin something first.`);\n  }\n  if (totalCharacters(kept) + incomingText.length > options.maxCharacters) {\n    throw new Error(`Pin budget exceeded: the pin set is limited to ${options.maxCharacters} characters in total.`);\n  }\n}\n\n// Pins cannot be written broader than the resource level: the reserved node\n// is anchored at (or below) the resource, and an org-scoped pin would only be\n// resolvable from the resource that created it, which is a silent-loss trap.\nfunction clampPinLevel(level: KnowledgeScopeLevel): KnowledgeScopeLevel {\n  return level === 'org' ? 'resource' : level;\n}\n\nfunction resolveWriteScope(options: PinnedToolsOptions, level?: KnowledgeScopeLevel): KnowledgeScope {\n  // An unscoped pin under a thread ceiling narrows to the ceiling instead of\n  // failing the assert on every call: pins are model-driven, so a config that\n  // makes the default request throw would be a tool error every turn.\n  let effective = clampPinLevel(level ?? options.defaultScope);\n  if (!level && options.maxScope === 'thread') effective = 'thread';\n  const scope = expandKnowledgeScope(options.scope, effective);\n  assertKnowledgeScopeWithinCeiling(scope, options.maxScope);","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/processors/observational-memory/subconscious/pinned.ts#L100-L136","documentation":"Pins are injected wholesale into context, so their combined size is capped at options.maxCharacters. assertBudget sums the characters of the pins that would remain (excluding the replaced pin, if any) plus the incoming text and throws when the total would exceed the cap. Unlike the count limit, this check also applies to replacements if the replacement text is too large.","triggerScenarios":"Pinning text whose length pushes totalCharacters(kept) + incomingText.length past maxCharacters — including oversized replacements.","commonSituations":"Pasting long documents or verbose summaries as pins; maxCharacters reduced in config below the existing pins' total size; repeated pinning of near-duplicate content that silently accumulates characters.","solutions":["Shorten/summarize the text before pinning","Unpin or replace verbose pins with tighter versions to free character budget","Raise options.maxCharacters in the pinned tools configuration","Enforce a client-side length check on pin text before calling the tool"],"exampleFix":"// before: pinning a 5000-char summary against a 4000-char budget\nawait pinTool.execute({ text: longSummary });\n// Error: Pin budget exceeded: the pin set is limited to 4000 characters in total.\n// after\nconst trimmed = summarize(longSummary, 800);\nawait pinTool.execute({ text: trimmed });","handlingStrategy":"validation","validationCode":"const pins = await listPins(memory);\nconst total = pins.reduce((n, p) => n + p.text.length, 0);\nif (total + text.length > maxCharacters) {\n  throw new Error(`Pinning ${text.length} chars would exceed the ${maxCharacters}-character pin budget.`);\n}","typeGuard":"function fitsCharacterBudget(pins, incoming, maxCharacters) {\n  const total = pins.reduce((n, p) => n + p.text.length, 0);\n  return total + incoming.length <= maxCharacters;\n}","tryCatchPattern":"try {\n  await pinTool.execute({ text });\n} catch (err) {\n  if (err.message.startsWith('Pin budget exceeded')) {\n    // shorten the text or free budget by unpinning/replacing verbose pins\n  } else throw err;\n}","preventionTips":["Summarize before pinning; pins should be terse","Check cumulative character usage when maxCharacters changes","Prune near-duplicate pins regularly","Reserve headroom (e.g. cap client-side at 80% of maxCharacters)"],"tags":["quota","knowledge-store","observational-memory"],"backgroundTag":"pin-budget-exceeded","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}