cd /news/ai-agents/the-none-that-broke-permissions-how-… · home topics ai-agents article
[ARTICLE · art-72330] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

The None That Broke Permissions: How a Single Null Crashed Hermes Agent Approvals

Aniruddha Adak, a full stack developer from Kolkata, fixed a critical bug in the NousResearch/hermes-agent open agent framework where a null response from the ACP protocol's request_permission function caused an AttributeError crash instead of safely denying permission. The fix, merged as PR #13457, adds a guard clause to return "deny" when the response is None, implementing a fail-safe pattern for permission checks.

read5 min views1 publishedJul 24, 2026

A tiny missing check, a silent crash, and a denied permission that should have been safe. This is the story of how I fixed it.

I am Aniruddha Adak, a full stack developer from Kolkata, working across Next.js

, React

, TypeScript

, Python

and AI agent tooling

. My GitHub profile is ** aniruddhaadak80** with over

google-gemini/gemini-cli

, openclaw/openclaw

, topoteretes/cognee

and NousResearch/hermes-agent

.Over the last two years I have been focusing on one thing, finding bugs that hide in plain sight, especially those that appear only on edge cases, cross platform setups and agent communication layers.

This post is my submission for DEV Summer Bug Smash 2026 in the Clear the Lineup track. It is also aiming for Best Use of Google AI.

The bug lived in NousResearch/hermes-agent, an open agent framework that connects AI models through the ACP protocol.

Inside the permission bridge, the function request_permission

is expected to return an object that tells Hermes whether to allow or deny an action. But sometimes, when an ACP client sends an empty response, that function returns None

.

The existing code trusted the result directly and accessed result.decision

without checking. When result

is None

, this throws an AttributeError. The agent crashes instead of denying safely. For a permission system, crashing is worse than denying. It leaves the user hanging and can break long running sessions.

In simple words, the code trusted that it would always get an answer. When it got silence, it broke.

I was working through open issues in hermes-agent

while exploring how agent permissions work on different clients. I noticed issue #13449 which described unexpected crashes during permission approval. I traced the flow from agent/transports

to the ACP callback and I saw there was no guard for None

. I reproduced it locally by mocking request_permission

to return None

and the crash happened instantly.

The bug was small, but its impact was large. Permission checks should never throw. They should always fail safe.

To reproduce, you connect via an ACP client that can send an empty response to permission requests. Then you trigger any tool that requires permission approval and you send an empty payload from the client. In the server logs you will see AttributeError: 'NoneType' object has no attribute 'decision'

.

My reproduction script was simple and it showed the problem clearly.

async def mock_request_permission():
    return None

result = await mock_request_permission()
decision = result.decision

With the old code, this crashes immediately. With the new code, it safely denies.

My merged PR is fix(permissions): handle None response from ACP request_permission in the hermes-agent repository. The PR link is https://github.com/NousResearch/hermes-agent/pull/13457 and it is merged.

I added a guard clause right after the await

. If the response is None

, I return "deny"

immediately. I added a dedicated unit test for this edge case and I kept the change minimal to avoid touching any other permission logic.

New logic looks like this

result = await request_permission(...)

if result is None:
    return "deny"

if result.decision == "allow":
    return "allow"
return "deny"

This is a classic fail safe pattern. In security code, when you do not know what happened, you deny. This pattern is simple, it is readable, and it prevents a whole class of crashes.

It does not change the happy path at all. It prevents a crash that could be triggered by any ACP client. It aligns with the principle of least privilege and it adds test coverage so it will not regress in future releases.

I built this fix using Antigravity, the agentic IDE by Google, and it changed how I debug. For codebase exploration I used Gemini 2.5 Pro inside Antigravity to map all places where request_permission

is called and where its return value is used. It gave me a full call graph in seconds and saved hours of manual search.

For reproduction scaffolding I prompted Antigravity to write a minimal async mock that returns None for ACP permission and triggers the approval callback. Antigravity generated the repro script and the test harness. For edge case reasoning I asked Gemini to list what else could be None in that bridge. It suggested checking for missing fields inside the result as a future hardening step.

I treat Antigravity as a pair programmer that never gets tired of reading large repos. It reads the whole codebase, I make the final decision. For the Best Use of Google AI category, this workflow shows how Gemini in Antigravity

can find a security relevant bug faster and make the fix safer with tests.

I have kept this table to only merged PRs and only bug related ones. No drafts, no closed unmerged, no docs typo fixes. This shows breadth and consistency.

Project PR Title Type PR Link
topoteretes/cognee fix(lancedb): automatically prefix windows paths to resolve OS Error 3 for long paths bug fix

All of these are merged and verified in production releases.

I ran pytest tests/ -q

locally and all tests passed. I added a new unit test that specifically covers the None

case. I verified that existing permission tests still pass and I checked cross platform impact as per the contributing guide. No OS specific change was needed for this fix.

The impact is clear. For stability, there are no more crashes when ACP clients send empty responses. For security, the permission system now fails safe to deny. For developer experience, there is now clear behavior for agent builders using custom ACP clients. For reliability, long running hermes sessions no longer die on this edge case.

Permission code should never trust external input. A single missing None

check can break an entire agent session. Writing a reproduction script first makes the fix obvious. Using an agentic IDE like Antigravity helps you see the full picture across many files. Small focused PRs get merged faster than large ones.

If you are starting with open source, start with bugs like this. Small, focused, high impact. That is how you learn and how you win trust from maintainers.

Links

GitHub is https://github.com/aniruddhaadak80

Dev profile is https://dev.to/aniruddhadak

PR discussed is https://github.com/NousResearch/hermes-agent/pull/13457

Challenge page is https://dev.to/bugsmash

── more in #ai-agents 4 stories · sorted by recency
── more on @aniruddha adak 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/the-none-that-broke-…] indexed:0 read:5min 2026-07-24 ·