권한을 망가뜨린 None: 단 하나의 Null이 어떻게 Hermes Agent 승인 시스템을 중단시켰나
요약
NousResearch/hermes-agent 프레임워크에서 ACP 프로토콜 사용 시 발생하는 AttributeError 버그를 분석하고 해결 과정을 다룹니다. 클라이언트의 빈 응답(None)이 권한 승인 시스템의 충돌을 유발하는 문제를 방어 로직을 통해 해결하는 방법을 설명합니다.
핵심 포인트
- ACP 클라이언트의 빈 응답이 Hermes Agent의 AttributeError를 유발함
- 권한 시스템은 예외 발생 시 반드시 안전하게 실패(fail-safe)해야 함
- NoneType 객체에 대한 방어 로직(guard) 부재가 시스템 충돌의 원인
- 에이전트 통신 계층에서의 에지 케이스 처리가 중요함
작은 체크 누락, 조용한 충돌, 그리고 안전했어야 할 권한 거부. 이것은 제가 이를 어떻게 해결했는지에 대한 이야기입니다.
자기소개 및 이 문제가 중요한 이유
저는 Kolkata 출신의 풀스택 개발자 Aniruddha Adak이며, Next.js, React, TypeScript, Python 및 AI agent 툴링 분야에서 활동하고 있습니다. 저의 GitHub 프로필은 **aniruddhaadak80**이며, Hacktoberfest 기간 동안 **238개 이상의 Pull Request (PR)**를 승인받았고 google-gemini/gemini-cli, openclaw/openclaw, topoteretes/cognee 및 NousResearch/hermes-agent와 같은 프로젝트에 활발히 기여하고 있습니다.
지난 2년 동안 저는 한 가지, 즉 눈에 잘 띄지 않는 곳에 숨어 있는 버그, 특히 에지 케이스 (edge cases), 크로스 플랫폼 설정 및 에이전트 통신 계층에서만 나타나는 버그를 찾는 데 집중해 왔습니다.
이 포스트는 Clear the Lineup 트랙의 DEV Summer Bug Smash 2026을 위한 제출물입니다. 또한 Best Use of Google AI 부문을 목표로 하고 있습니다.
버그의 정체
이 버그는 **ACP 프로토콜 (ACP protocol)**을 통해 AI 모델을 연결하는 오픈 에이전트 프레임워크인 NousResearch/hermes-agent에 존재했습니다.
권한 브리지 (permission bridge) 내부에서, request_permission 함수는 Hermes에게 특정 동작을 허용할지 거부할지를 알려주는 객체를 반환해야 합니다. 하지만 때때로 ACP 클라이언트가 빈 응답을 보낼 때, 해당 함수는 None을 반환합니다.
기존 코드는 결과를 직접 신뢰하여 확인 절차 없이 result.decision에 접근했습니다. result가 None일 때, 이는 AttributeError를 발생시킵니다. 에이전트는 안전하게 거부하는 대신 충돌(crash)이 발생합니다. 권한 시스템에서 충돌은 거부하는 것보다 더 나쁩니다. 이는 사용자를 대기 상태로 방치하고 장시간 실행 중인 세션을 끊어버릴 수 있습니다.
간단히 말해, 코드는 항상 답변을 받을 것이라고 믿었습니다. 침묵을 마주했을 때, 코드는 망가졌습니다.
버그를 발견한 방법
버그를 발견한 방법
저는 다양한 클라이언트에서 에이전트 권한(agent permissions)이 어떻게 작동하는지 탐색하며 hermes-agent의 열린 이슈들을 검토하고 있었습니다. 그 과정에서 권한 승인 중 예상치 못한 충돌을 설명하는 #13449라는 이슈를 발견했습니다. 저는 agent/transports부터 ACP 콜백까지의 흐름을 추적했고, None에 대한 방어 로직(guard)이 없다는 것을 확인했습니다. 제가 직접 request_permission을 호출하여 None을 반환하도록 모킹(mocking)해보고자 하자 충돌이 즉시 발생했습니다.
버그 자체는 작았지만, 그 영향력은 컸습니다. 권한 검사는 절대로 예외를 던져서는 안 됩니다. 항상 안전하게 실패해야 합니다 (fail safe).
문제 재현하기
재현하려면, 권한 요청에 빈 응답을 보낼 수 있는 ACP 클라이언트를 통해 연결해야 합니다. 그런 다음 권한 승인이 필요한 모든 도구(tool)를 트리거하고 클라이언트에서 빈 페이로드(payload)를 전송하면 됩니다. 서버 로그에서는 AttributeError: 'NoneType' object has no attribute 'decision'라는 오류가 보일 것입니다.
제가 사용한 재현 스크립트는 간단했고 문제를 명확하게 보여주었습니다.
async def mock_request_permission():
return None
...
이전 코드에서는 이것이 즉시 충돌을 일으켰습니다. 새로운 코드에서는 안전하게 거부합니다 (safely denies).
수정 사항, 기술 심층 분석
제가 병합(merged)한 PR은 hermes-agent 리포지토리의 fix(permissions): handle None response from ACP request_permission입니다. PR 링크는 https://github.com/NousResearch/hermes-agent/pull/13457이며 이미 병합되었습니다.
제가 변경한 내용
저는 await 직후에 방어 로직(guard clause)을 추가했습니다. 응답이 None이면 즉시 `
이는 정상적인 경로 (happy path)에는 전혀 영향을 주지 않습니다. 대신 어떤 ACP 클라이언트라도 유발할 수 있는 크래시 (crash)를 방지합니다. 또한 최소 권한 원칙 (principle of least privilege)에 부합하며, 테스트 커버리지 (test coverage)를 추가하여 향후 릴리스에서 기능이 퇴보 (regress)하지 않도록 합니다.
Google Antigravity와 Google AI를 활용한 방법
저는 Google의 에이전트형 IDE인 Antigravity를 사용하여 이 수정 사항을 구축했으며, 이는 저의 디버깅 방식을 바꾸어 놓았습니다. 코드베이스 탐색을 위해 Antigravity 내부의 Gemini 2.5 Pro를 사용하여 request_permission이 호출되는 모든 지점과 그 반환 값이 사용되는 지점을 매핑했습니다. 이를 통해 단 몇 초 만에 전체 호출 그래프 (call graph)를 얻을 수 있었고, 수 시간의 수동 검색 시간을 절약했습니다.
재현 스캐폴딩 (reproduction scaffolding)을 위해, Antigravity에 ACP 권한에 대해 None을 반환하고 승인 콜백 (approval callback)을 트리거하는 최소한의 비동기 모크 (async mock)를 작성하도록 프롬프트를 입력했습니다. Antigravity는 재현 스크립트와 테스트 하네스 (test harness)를 생성했습니다. 엣지 케이스 (edge case) 추론을 위해, Gemini에게 해당 브릿지에서 None이 될 수 있는 다른 요소가 무엇인지 목록을 만들어 달라고 요청했습니다. Gemini는 향후 보안 강화 단계로서 결과 내부의 누락된 필드를 확인하는 것을 제안했습니다.
저는 Antigravity를 대규모 리포지토리 (repo)를 읽는 데 지치지 않는 페어 프로그래머 (pair programmer)로 취급합니다. Antigravity가 전체 코드베이스를 읽으면, 최종 결정은 제가 내립니다. Google AI의 최적 활용 (Best Use of Google AI) 카테고리와 관련하여, 이 워크플로우는 Antigravity의 Gemini가 어떻게 보안 관련 버그를 더 빠르게 찾아내고, 테스트를 통해 수정 사항을 더 안전하게 만들 수 있는지를 보여줍니다.
병합된 모든 버그 수정 PR (병합된 것만 포함)
이 표에는 병합된 (merged) PR과 버그 관련 (bug related) PR만을 유지했습니다. 초안 (draft), 병합되지 않고 닫힌 (closed unmerged) PR, 문서 오타 수정 등은 포함하지 않았습니다. 이는 저의 작업 범위와 일관성을 보여줍니다.
| 프로젝트 | PR 제목 | 유형 | PR 링크 |
|---|---|---|---|
| topoteretes/cognee | fix(lancedb): automatically prefix windows paths to resolve OS Error 3 for long paths | bug fix | https://github.com/topoteretes/cognee/pull/3123 |
| ... | |||
| 이 모든 항목은 병합되었으며 프로덕션 릴리스에서 검증되었습니다. |
테스트 및 검증
로컬에서 pytest tests/ -q를 실행하였고 모든 테스트를 통과했습니다. None 케이스를 구체적으로 다루는 새로운 단위 테스트 (unit test)를 추가했습니다. 기존의 권한 테스트들이 여전히 통과하는 것을 확인했으며, 기여 가이드 (contributing guide)에 따라 크로스 플랫폼 (cross platform) 영향을 점검했습니다. 이 수정 사항을 위해 OS 특정적인 변경은 필요하지 않았습니다.
수정의 영향 (Impact of the fix)
영향은 명확합니다. 안정성 측면에서는 ACP 클라이언트가 빈 응답을 보낼 때 더 이상 크래시 (crash)가 발생하지 않습니다. 보안 측면에서는 권한 시스템이 이제 거부하는 방향으로 안전하게 실패 (fail safe)합니다. 개발자 경험 측면에서는 커스텀 ACP 클라이언트를 사용하는 에이전트 빌더 (agent builders)를 위한 명확한 동작이 제공됩니다. 신뢰성 측면에서는 장시간 실행되는 Hermes 세션이 이 에지 케이스 (edge case)로 인해 더 이상 중단되지 않습니다.
배운 점
권한 코드는 외부 입력을 절대 신뢰해서는 안 됩니다. 단 하나의 누락된 None 체크가 에이전트 세션 전체를 망가뜨릴 수 있습니다. 재현 스크립트 (reproduction script)를 먼저 작성하면 수정 방법이 명확해집니다. Antigravity와 같은 에이전틱 IDE (agentic IDE)를 사용하면 여러 파일에 걸친 전체 그림을 보는 데 도움이 됩니다. 작고 집중된 PR (Pull Request)은 큰 PR보다 더 빠르게 병합됩니다.
오픈 소스를 시작하고 싶다면, 이와 같은 버그부터 시작하세요. 작고, 집중되어 있으며, 영향력이 큽니다. 그것이 배우는 방법이며, 메인테이너 (maintainer)들의 신뢰를 얻는 방법입니다.
링크
GitHub: https://github.com/aniruddhaadak80
Dev 프로필: https://dev.to/aniruddhadak
논의된 PR: https://github.com/NousResearch/hermes-agent/pull/13457
챌린지 페이지: https://dev.to/bugsmash
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기