I haven't seen this method widely documented (maybe it is?)

When a [Bash Tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool) use fails due to a missing or wrong command, normally Claude will invent another way to achieve the same thing. Often that flexibility is good, but sometimes you want to prevent it.

Below, I describe how to "stub" missing executables with extra runtime context to keep Claude on track.


UPDATE: For real, less than 12 hours after posting this, I learned more about Claude Code Hooks, which can do BASICALLY the same thing I suggest here. With Claude, using "hooks" is generally better and more flexible than the "stubbing" method I describe below.

I'm leaving the post up anyway. I still like the concept. It provides good fallback behavior, and it remains useful with agent systems that lack hooks.


The Problem

Claude Code and pals often invoke shell commands that don't exist (or shouldn't be used). This happens a lot with package managers. Sometimes it leads down a rabbit hole of very obscure or undesirable installation methods.

Python famously has a lot of ways to manage environments. My projects all use uv for dependencies, but sometimes Claude still tries pip or pip3 instead of uv.

The CLAUDE.md file exists for precisely this purpose. It allows you to give directives about which commands to prefer, and which to avoid. But in my experience, that only works 80-90% of the time. Sometimes Claude will still try an executable that is either missing or wrong.

My guess is context pollution. In long sessions where Claude has loaded a lot of documentation into the context, there might be conflicting instructions from the docs that confuse or override preferences originally set in CLAUDE.md.

Tool Time

When a tool call fails, due to a missing executable, it looks like this:

 Bash(pip install fastapi) timeout: 1m 0s
    Error: Exit code 1

Since the pip executable doesn't exist, the command fails.

And now.. Claude will do.. SOMETHING!

After it ponders that error message, usually it will proceed with trying a different installation method. I can tell you from experience, it will usually attempt one of these:

  • using pip3
  • using poetry
  • using uv
  • installing pip using the system package manager (apt, homebrew, etc)
  • .. even more!

Exactly which command the agent chooses next will depend on the project setup, the current context, and also the inherent randomness of LLMs.

Wrong commands waste time and tokens. Claude is quite good at installing packages. But even when "successful", sometimes it chooses a bad path to get there. This could lead to wrong software versions, mixing package managers, broken paths, or much worse.

When Claude takes a step in the wrong direction, we'd like to gently steer it back.

The solution

There is a pretty simple fix for this:

  • put a "stub" executable named "pip" on the path
  • exit with an error
  • print a message that explicitly tells Claude what to do instead

Below is that same tool use, after I have stubbed the pip executable with this technique.

 Bash(pip install fastapi) timeout: 1m 0s
    Error: Exit code 1
      prefer uv add

Pretty simple!

Previously the command was absent, now it exists, but exits with an error message.

We've just added one extra line to the output. That helpful message is all that Claude needs. It tells the agent exactly what to do next (in this case, to run uv add instead)

Creating the stubs

Creating these stubs is easy. Below is the one-liner that I'm using in my Dockerfile.

To be honest, here I just asked ChatGPT to write a shell command and create the stub for me.

mkdir -p ~/.local/bin \
 && printf '#!/usr/bin/env sh\necho "prefer uv add" >&2\nexit 1\n' \
    > ~/.local/bin/pip \
 && chmod +x ~/.local/bin/pip

Now if Claude tries to run pip again in the future (and it will), the corrective error message will immediately steer it back towards uv add for package management.

Of course, this idea can be applied to any command, not just pip

Simple trick, but I thought it was kinda neat.

Some quick notes

First, this assumes that ~/.local/bin is on the PATH, that the stub is in that directory, that it's executable, and that it has higher PATH precedence than any other command with the same name. Normal unix behavior, but worth mentioning.

Second, this assumes the absence of the actual pip executable. If the original pip is present, my suggestion might "shadow" it, depending on the PATH settings. Shadowing may or may not be desirable.

Third, this isn't very DRY. I'm describing the placement of a hint into an executable stub file that potentially lives far away from the rest of the agent configuration Just something to consider.

Fourth, this method provides a strong hint, but it's not a guarantee. When a tool use fails, even with this enhanced error message, Claude might still chose to do something totally different. The permission rules documentation describes another method to block specific tool uses.

Finally, as mentioned at the beginning, Claude Code Hooks provide a better way to do this for most situations. Other agent-based software may have a similar feature, so be sure to check.