Avoid the Dangers of Settings Pollution in Subagents, Hooks, and Scripts

When building complex automations with Claude, it's crucial to understand that any settings defined for your main agent—including powerful hooks—are inherited by any sub-agents it creates. This can lead to a dangerous "worst-case scenario": a PostToolUse hook that calls Claude again can trigger an infinite recursive loop, spawning endless processes and potentially crashing your system and burning through your API tokens.

This lesson demonstrates this exact problem and provides a clear, effective solution. You'll learn how to break the recursive chain by using the --settings flag to provide your sub-agent with a separate, minimal configuration file that explicitly disables all hooks.

Workflow demonstrated in this lesson:

  • Create a Recursive Hook: Set up a PostToolUse hook that calls a script, which in turn invokes another instance of Claude.
  • Trigger the Infinite Loop: Run a simple command to trigger the hook and observe how it rapidly spawns new Claude processes.
  • Introduce the --settings Flag: Modify the sub-agent call in the script to include a --settings flag pointing to a new configuration file (e.g., no-hooks.json).
  • Disable Hooks for the Sub-agent: In the new settings file, add the "disableAllHooks": true property.
  • Verify the Fix: Run the command again to confirm that the sub-agent uses the new settings, the hook is not triggered, and the infinite loop is prevented.

Key benefits:

  • Understand Setting Inheritance: Learn how project settings are passed down to sub-agents.
  • Prevent Dangerous Loops: Master the technique to stop recursive hooks and secure your automations.
  • Gain Granular Control: Use separate settings files to give sub-agents different capabilities than the main agent.
  • Build More Stable AI Agents: Ensure your complex workflows are safe, predictable, and efficient.

Summary

This lesson demonstrates a critical security consideration when working with Claude sub-agents: they inherit project-level settings, including hooks. This can create a dangerous infinite loop if a hook is configured to call Claude again. The video first showcases this "worst-case scenario" by setting up a PostToolUse hook that triggers a script, which in turn invokes a new Claude instance, leading to an uncontrollable cascade of processes. The solution presented is to use the --settings flag when calling the sub-agent from the script. This flag points to a separate configuration file (e.g., no-hooks.json) containing the property "disableAllHooks": true, which effectively breaks the recursive chain and prevents the infinite loop, securing the automation.

Prompts

Please check the git status.
Create a timestamped .txt file

Terminal Commands

pkill -f "claude"
claude

Code Snippets

The initial settings.local.json file configures a hook that will run after any tool is used, creating the potential for a recursive loop.

{ "hooks": { "PostToolUse": [ { "matcher": "*", "hooks": [ { "type": "command", "command": "bun .claude/hooks/index.ts" } ] } ] } }

The hook script (index.ts) itself calls Claude again, which inherits the hook settings and causes the infinite loop.

import { $ } from "bun"; await $`claude --print --model haiku "Check on the git status then write it out to a timestamped .txt file"`;

To fix this, the script is updated to pass a --settings flag, pointing to a file with no hooks.

import { $ } from "bun"; await $`claude --print --model haiku "Check on the git status then write it out to a timestamped .txt file" --settings no-hooks.json`;

The no-hooks.json file contains a single property to disable all hooks for the sub-agent, breaking the loop.

{ "disableAllHooks": true }
The Essential Guide to Claude Code Skills
Lesson 8 of 9

The Essential Guide to Claude Code Skills

Share with a coworker

Transcript

[00:00] So I've set up a post tool use hook which will run this script we've defined with bun. And inside of this script we're going to invoke clod again in non-interactive mode so that it will just do the task we requested and give it a default prompt of checking the git status and writing a timestamp file. Now I'm going to have this pkill command ready and I'm going to have my activity monitor ready because thinking ahead if I start a new clod session and I tell it something like please check the git status, Running this tool is going to invoke this hook, which is going to run clod, which is going to run this tool, which is going to invoke this hook, which is going to run clod, tool, hook. Now I will do this once just to prove how dangerous this is. I'll hit enter.

[00:48] I'll bring over the activity monitor. You'll see another clod, and another, and another, and another, and another, and that's enough for me to call this quits here. So we shut those all down. Since pkill will terminate any process that has Claude in it, I think that demonstrated how dangerous this can be. The key thing to mention here is that these settings defined in your project will be inherited by this main agent but also by any sub-agent that is called inside of the same project.

[01:21] So whether your settings define what your allowed tools are, what hooks to run, or anything else you need to be extremely careful when defining sub-agents and scripts and hooks and anything else that could call clod to make sure it's using the settings that you want. Now the solution for this particular problem is in your hook you want to pass a flag here called ''settings'', and I'm going to point it to a file called no-hooks.json and create that file next to the script because this script is defined inside of the hooks folder. That's its current working directory. So we'll make the settings relative to that file. I'll call this file no-hooks.json and I'm going to add the property to it of disableAllHooksTrue.

[02:06] And that means this time if I start a new cloud session I'll run the exact same command and you'll see we have one cloud running which is this one. I'll hit enter, bring over the activity monitor. We'll allow it to edit files because we want that timestamp file. And you may see some status line popped up because I'm using a package for my status line which has the word Clawed in it. But other than that we completely avoided this problem and we just have this single instance of Clawed running.

[02:37] So while this is pretty close to worst case scenario there are many other scenarios you need to be cognizant of when defining your settings for your main agent and being aware that your sub-agents, or anytime you manually invoke Claude yourself, will also inherit those settings.