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

> Source: <https://dev.to/aniruddha_adak/the-none-that-broke-permissions-how-a-single-null-crashed-hermes-agent-approvals-189f>
> Published: 2026-07-24 16:32:00+00:00

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.

``` python
async def mock_request_permission():
    return None

# old logic that crashes
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](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](https://github.com/aniruddhaadak80)

Dev profile is [https://dev.to/aniruddhadak](https://dev.to/aniruddhadak)

PR discussed is [https://github.com/NousResearch/hermes-agent/pull/13457](https://github.com/NousResearch/hermes-agent/pull/13457)

Challenge page is [https://dev.to/bugsmash](https://dev.to/bugsmash)
