{"slug": "the-none-that-broke-permissions-how-a-single-null-crashed-hermes-agent-approvals", "title": "The None That Broke Permissions: How a Single Null Crashed Hermes Agent Approvals", "summary": "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.", "body_md": "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.\n\nI am **Aniruddha Adak**, a full stack developer from Kolkata, working across `Next.js`\n\n, `React`\n\n, `TypeScript`\n\n, `Python`\n\nand `AI agent tooling`\n\n. My GitHub profile is ** aniruddhaadak80** with over\n\n`google-gemini/gemini-cli`\n\n, `openclaw/openclaw`\n\n, `topoteretes/cognee`\n\nand `NousResearch/hermes-agent`\n\n.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.\n\nThis 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**.\n\nThe bug lived in **NousResearch/hermes-agent**, an open agent framework that connects AI models through the **ACP protocol**.\n\nInside the permission bridge, the function `request_permission`\n\nis 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`\n\n.\n\nThe existing code trusted the result directly and accessed `result.decision`\n\nwithout checking. When `result`\n\nis `None`\n\n, 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.\n\nIn simple words, the code trusted that it would always get an answer. When it got silence, it broke.\n\nI was working through open issues in `hermes-agent`\n\nwhile 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`\n\nto the ACP callback and I saw there was no guard for `None`\n\n. I reproduced it locally by mocking `request_permission`\n\nto return `None`\n\nand the crash happened instantly.\n\nThe bug was small, but its impact was large. Permission checks should **never** throw. They should always fail safe.\n\nTo 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'`\n\n.\n\nMy reproduction script was simple and it showed the problem clearly.\n\n``` python\nasync def mock_request_permission():\n    return None\n\n# old logic that crashes\nresult = await mock_request_permission()\ndecision = result.decision\n```\n\nWith the old code, this crashes immediately. With the new code, it safely denies.\n\nMy 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.\n\nI added a guard clause right after the `await`\n\n. If the response is `None`\n\n, I return `\"deny\"`\n\nimmediately. I added a dedicated unit test for this edge case and I kept the change minimal to avoid touching any other permission logic.\n\nNew logic looks like this\n\n```\nresult = await request_permission(...)\n\nif result is None:\n    return \"deny\"\n\nif result.decision == \"allow\":\n    return \"allow\"\nreturn \"deny\"\n```\n\nThis 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.\n\nIt 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.\n\nI 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`\n\nis called and where its return value is used. It gave me a full call graph in seconds and saved hours of manual search.\n\nFor 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.\n\nI 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`\n\ncan find a security relevant bug faster and make the fix safer with tests.\n\nI 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.\n\n| Project | PR Title | Type | PR Link |\n|---|---|---|---|\n| topoteretes/cognee | fix(lancedb): automatically prefix windows paths to resolve OS Error 3 for long paths | bug fix |\n|\n\n*All of these are merged and verified in production releases.*\n\nI ran `pytest tests/ -q`\n\nlocally and all tests passed. I added a new unit test that specifically covers the `None`\n\ncase. 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.\n\nThe 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.\n\nPermission code should never trust external input. A single missing `None`\n\ncheck 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.\n\nIf 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.\n\n**Links**\n\nGitHub is [https://github.com/aniruddhaadak80](https://github.com/aniruddhaadak80)\n\nDev profile is [https://dev.to/aniruddhadak](https://dev.to/aniruddhadak)\n\nPR discussed is [https://github.com/NousResearch/hermes-agent/pull/13457](https://github.com/NousResearch/hermes-agent/pull/13457)\n\nChallenge page is [https://dev.to/bugsmash](https://dev.to/bugsmash)", "url": "https://wpnews.pro/news/the-none-that-broke-permissions-how-a-single-null-crashed-hermes-agent-approvals", "canonical_source": "https://dev.to/aniruddha_adak/the-none-that-broke-permissions-how-a-single-null-crashed-hermes-agent-approvals-189f", "published_at": "2026-07-24 16:32:00+00:00", "updated_at": "2026-07-24 17:02:39.533066+00:00", "lang": "en", "topics": ["ai-agents", "ai-safety", "ai-tools", "developer-tools"], "entities": ["Aniruddha Adak", "NousResearch", "hermes-agent", "ACP protocol", "Google", "Antigravity", "Gemini 2.5 Pro"], "alternates": {"html": "https://wpnews.pro/news/the-none-that-broke-permissions-how-a-single-null-crashed-hermes-agent-approvals", "markdown": "https://wpnews.pro/news/the-none-that-broke-permissions-how-a-single-null-crashed-hermes-agent-approvals.md", "text": "https://wpnews.pro/news/the-none-that-broke-permissions-how-a-single-null-crashed-hermes-agent-approvals.txt", "jsonld": "https://wpnews.pro/news/the-none-that-broke-permissions-how-a-single-null-crashed-hermes-agent-approvals.jsonld"}}