thedotmack/claude-mem · info
is forbidden from tool use (claude-mem hard lockdown).
Error message
${input.source} is forbidden from tool use (claude-mem hard lockdown). What it means
This is the deny message returned by the hardened SDK canUseTool hook. claude-mem's Observer and KnowledgeAgent sessions are deliberately built with a hard lockdown: every tool use is denied and logged, because these agent sessions must only observe/compress, never act. The message is informational-by-design, not a bug — it records that a tool-use attempt was blocked.
Solutions
- If you are a user seeing this in logs: no action needed — the security boundary worked; check the audit log via recordObserverToolAttempt for what was attempted
- If you expected real tool use: do not route that workload through buildHardenedSdkOptions/Observer sessions; use a normal interactive session
- If you are developing: confirm the call site legitimately should be locked down; do not weaken the deny — adjust the session type instead
Example fix
// before: observer tries to use tools and gets denied
const options = buildHardenedSdkOptions({ source: 'Observer', ... });
// after: use a non-hardened path only when tool use is intentional
const options = buildSdkOptionsForInteractiveAgent({ ... }); // if tool use is expected Defensive patterns
Strategy: validation
Type guard
function isHardenedSource(s: string): s is 'Observer' | 'KnowledgeAgent' {
return s === 'Observer' || s === 'KnowledgeAgent';
} Try / catch
if (result.behavior === 'deny') {
logger.warn('Tool use denied by hard lockdown:', result.message);
// continue without executing the tool; do not retry
} Prevention
- Never route workloads that need tools through buildHardenedSdkOptions
- Treat this deny message in logs as expected security behavior, not a fault
- Review recordObserverToolAttempt audit entries if you see frequent denials
- Keep tool-requiring agents on separate, non-hardened session construction
When it happens
Trigger: Any Observer or KnowledgeAgent SDK session (built via buildHardenedSdkOptions) attempts to call ANY tool (Bash, Write, WebFetch, etc.), triggering the canUseTool callback which unconditionally returns behavior:'deny'.
Common situations: Model spontaniously tries to run Bash or edit a file during an observation session; a prompt injection in watched content convinces the observer to call a tool; misconfigured session that was meant to be an interactive Claude session got built with hardened options.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Access denied: " " resolves outside the workspace ( ). MCP…
- Admin endpoints are only accessible from localhost
- Anthropic API rejected request with HTTP 400: this model…
- Could not restrict permissions on
- Forbidden
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/c80c2e9283ee2964.
Report an issue: GitHub.
Appendix: source
Thrown at src/sdk/hardened-options.ts:145
const canUseTool: Options['canUseTool'] = async (toolName, toolInput) => {
recordObserverToolAttempt({
source: input.source,
sessionDbId: input.sessionDbId,
contentSessionId: input.contentSessionId,
project: input.project,
tool_name: toolName,
tool_input: toolInput,
result: 'denied',
});
// Real-time visibility for the persistent audit trail. The append-only log
// (recordObserverToolAttempt above) is the authoritative record; this WARN
// surfaces the attempt in the live worker log for incident detection.
logger.warn('SECURITY', `Blocked tool use by ${input.source}: ${toolName}`, {
sessionId: input.sessionDbId,
source: input.source,
tool_name: toolName,
});
return {
behavior: 'deny',
message: `${input.source} is forbidden from tool use (claude-mem hard lockdown).`,
};
};
return {
model: input.model,
cwd: input.cwd ?? OBSERVER_SESSIONS_DIR,
env: input.env,
pathToClaudeCodeExecutable: input.pathToClaudeCodeExecutable,
...(input.abortController ? { abortController: input.abortController } : {}),
...(input.resume ? { resume: input.resume } : {}),
...(input.spawnClaudeCodeProcess ? { spawnClaudeCodeProcess: input.spawnClaudeCodeProcess } : {}),
// Observer thinking is behavior-only and does not participate in the lockdown boundary.
...(input.source === 'Observer' ? { thinkingConfig: { type: 'disabled' as const } } : {}),
// === Tool lockdown (defense-in-depth) ===
tools: [], // belt: disable ALL built-in toolsView on GitHub (pinned to d8bc9755e7)