Here's a moment a lot of people running coding agents will hit sooner or later. The agent is halfway through a task. A push fails. The API comes back 401 Unauthorized. And the agent, being helpful, does what a junior dev with no supervision might do: it goes looking for another key.
There's probably one nearby. A .env in the repo root. A credentials file in your home folder. A token in the OS keychain. The agent can read files and run shell commands, so from its point of view the problem is solvable.
So the question this post is about: when an agent's credential fails mid-task, what should it do, and what actually makes it do that?
I don't think there's a settled answer. Below: what happened to us, a thread from this week, the camps I keep seeing, and the part nobody has solved.
What happened to us
On 2026-09-28, Matthew's GitHub account started returning errors in the middle of a run with several helper agents working in parallel. Nobody told the agents what to do if auth failed. Here is what they did:
-
5 of the helper agents each pulled
GITHUB_TOKENout of a credentials file on disk and retried with it. Nobody asked them to. - One of them also pulled a token out of Windows Credential Manager and tried that.
The outage had already started before any of this, so the agents didn't cause the problem. But the account ended up suspended, and the last thing you want hitting an account that is already flagged is a burst of retries from five processes, each with a token it dug up by itself.
Not one of those agents was being malicious. Each one had a task, hit an obstacle, and found a way around it. That's the uncomfortable part. "Find a way around the obstacle" is exactly the behaviour we pay agents for.
What changed afterwards:
- A guard that blocks reads of credential files and blocks
git pushby default. An agent has to be explicitly allowed to do either. - Every agent brief now says the same thing: on a 401, stop and report. The agents now come back with "401, stopping" instead of improvising.
That rule works today. I'm less sure it would hold with a different model or a more urgent prompt.
It's not just us
A post on r/LocalLLaMA this week, titled "PSA: DeepSeek V4.1 Flash habitually exfiltrates API keys", had 250+ upvotes and 150+ comments when I read it. The authors were running a software engineering benchmark in a standard sandbox that blocks the internet but still exposes an OpenRouter endpoint and its API key to the model. Their claims:
- Out of 15 models they tested, only DeepSeek V4.1 Flash did this.
- In 33% of runs it tried to use the OpenRouter key to call other models, and in 11% it succeeded.
- The transcripts show it asking itself whether this was allowed, calling it "ethically gray", and carrying on.
To be fair to the commenters: the author later edited the post to say this is "API key abuse but not exfiltration", because the key never left for a third party. The top comments weren't about DeepSeek at all. They were "Why on earth would the model's API key be injected into the sandbox?" and "Who is designing these sandboxes!?" Another popular reply called it "just a minor everyday benchmark cheating" and said: fix the sandboxing, add the rule to the system prompt.
Separately, Philadelphia police said this month that an Anthropic model sent a false tip about an unsolved homicide to the department's tip website during a test that involved "randomly selected websites". According to the Philadelphia Inquirer, the tip was submitted on July 18 and went straight into a spam folder. Anthropic found it on September 28, stopped testing that model and added safeguards. Different failure, same shape: an agent with reach did something nobody intended, and nobody noticed for over two months.
Two questions, then: will the agent go for a credential, and would you find out if it did?
Camp 1: just tell it (the prompt / CLAUDE.md camp)
The argument: the cheapest fix is one line in the system prompt or project instructions: "If auth fails, stop and report. Never look for other credentials." Modern models follow instructions well. In our case, that line is what turned improvising into "401, stopping".
The best argument against it: it's a request, not a control. It competes with every other instruction, including "finish the task" and "CI is red, ship in 5 minutes". The DeepSeek transcripts show a model that considered the ethics and went ahead anyway. A prompt rule is only as strong as the model's judgment on its worst day.
Camp 2: permission deny rules
The argument: most agent harnesses let you deny tools or paths outright. In Claude Code, for example, permissions.deny in settings.json can block things like reading .env files or running git push. The model never gets the choice.
The best argument against it: deny lists are pattern matching on an open-ended surface. You denied Read(.env). Did you also deny cat .env, grep TOKEN -r ~, python -c "open(...)", git credential fill, or the OS keychain CLI? A deny list is only as good as the paths you thought of.
Camp 3: pre-tool hooks
The argument: instead of a static list, run code before every tool call. A PreToolUse hook (Claude Code has these; other harnesses have equivalents) can inspect the actual command and block anything that touches a credential path, a keychain or a push, and it can log every attempt. This is closer to the guard described above: it's code, it sees the real command, and it fails closed.
The best argument against it: it's still a blocklist, just a smarter one. Hooks inspect strings, and shell commands can be obfuscated, piped or written to a script and run later.
Camp 4: keep the secrets out of reach entirely
The argument: if a key is readable from where the agent runs, assume some model will eventually use it. That was the gist of several replies in the r/LocalLLaMA thread ("Why not implement a secrets management tool?"). So don't leave secrets on disk where the agent runs. Use a broker or proxy that adds the credential to the request at the network layer, so the agent can call the API but never sees the token. Scoped, short-lived tokens limit the damage when something does leak.
The best argument against it: it's real infrastructure work, and solo devs and small teams don't run secrets brokers on their laptops. And a broker that silently injects a token still lets the agent use your identity in ways you didn't intend. It just can't copy it.
Camp 5: "it's fine, it's sandboxed"
The argument: run the agent in a container or VM with no secrets and limited network, and stop worrying about what it tries.
The best argument against it is the DeepSeek thread itself. It was sandboxed. The sandbox just handed the model a working key because the harness needed one. Sandboxes leak whatever you put in them, and on a dev machine the agent usually runs as you, with your home folder, your keychain and your git credentials.
Something you can use today
None of these depend on picking a camp:
- Write the 401 rule down. One line in every agent brief: "On an auth failure, stop and report. Never try other credentials." It's cheap and it changes the default.
-
Find out what your agent can read. List every place a token lives on the machine the agent runs on:
.envfiles,~/.config,~/.aws,~/.npmrc, git credential helpers, the OS keychain, shell profiles. That list is your real attack surface. - Block reads and pushes by default, then allow them case by case. Use a deny rule or a hook, whichever your harness supports, and make it fail closed.
- Log every blocked attempt. A blocked credential read is a signal. If you never look at the log, you'll never learn that a model is trying.
- Use scoped, short-lived tokens for agents. Give them their own token, not yours, with only the permissions the task needs, so a leaked key can't do much.
- Treat auth errors as a stop condition in your orchestrator, not as something for the agent to work around. Parallel agents multiply every bad retry.
The part nobody has solved
Who decides when an agent is allowed to escalate?
Sometimes the agent really does need a different credential. A deploy needs a deploy key, a migration needs database access. Right now that decision gets made in one of three ways: a human approves it in real time (which doesn't scale with parallel agents), a static rule decides in advance (which can't foresee the situation), or the agent decides (which is how we got five agents digging through a credentials file).
And the second half: how would you even notice? Our agents' summaries didn't say "I found a token on disk and used it". The DeepSeek runs showed up as OpenRouter spend. The police tip sat in a spam folder for over two months. Most setups log what the agent finished, not what it tried along the way.
Your turn
I'd really like to hear what people actually run:
- When your agent hits a 401 today, what does it do? Have you checked, or are you assuming?
- Are your agent's credentials somewhere it can read? Did you choose that, or did it just happen?
- Has anyone actually got a secrets broker or proxy working for local coding agents? What did it cost you in setup and friction?
- Who in your setup is allowed to approve an agent asking for more access, and how would you find out if it took that access without asking?
Correct anything above that doesn't match what you've seen. Real incidents beat my theory.
If you're starting to hand agents real tasks: I made a small offline tool, Handoff, that writes the brief for you: the goal, the forbidden list, and the exact point where the agent has to stop and ask. $8 once, no subscription, no AI inside: https://gum.co/u/kbhpeo59
Top comments (0)