Windows에서 Claude Code 훅이 작동하지 않을 때: 원인과 해결 방법
Mac에서 잘 실행되던 훅이 Windows에서는 아무 동작도 하지 않거나, 더 나쁘게는 모든 명령을 차단합니다. 원인은 거의 항상 셸입니다. Claude Code가 Windows에서 훅을 실행하는 방식과 훅을 안정적으로 만드는 방법을 알아봅니다.
핵심 요약
Windows에서 Claude Code는 훅의 command를 Git Bash로 실행하고, Git Bash가 설치되어 있지 않으면 PowerShell로 실행합니다. "shell": "powershell"이나 "shell": "bash"로 어느 쪽인지 명시하고, stdout에는 JSON 객체 하나만 출력하며(프로필의 echo 금지), 차단하려면 종료 코드 2를 사용하고, Claude Code를 업데이트하세요(여러 Windows 훅 버그가 2.1.310까지의 버전에서 수정되었습니다). 디버깅에는 /hooks와 claude --debug를 사용합니다.
어떤 셸이 훅을 실행하는가
Claude Code는 command 문자열을 셸에 넘깁니다. macOS와 Linux에서는 sh -c, Windows에서는 Git Bash, Git Bash가 설치되어 있지 않으면 PowerShell입니다. 훅 레퍼런스의 이 한 문장이 "내 Mac에서는 되는데"라는 보고 대부분을 설명합니다. PowerShell용으로 작성한 명령은 Git Bash에서 실패하고, bash 스크립트는 PowerShell에서 실패하며, 어느 쪽이 실행될지는 Git for Windows가 우연히 설치되어 있는지에 달려 있습니다.
shell 필드로 추측을 없애세요. "bash" 또는 "powershell"을 받습니다.
Git Bash를 쓰고 싶은데 Claude Code가 찾지 못한다면 Git for Windows를 설치하거나 CLAUDE_CODE_GIT_BASH_PATH를 해당 위치로 설정한 뒤 새 세션을 시작하세요.
증상과 원인
| 나타나는 현상 | 가능한 원인 | 해결 방법 |
|---|---|---|
| 훅이 전혀 실행되지 않는 것 같음 | 오래된 Claude Code 버전, 잘못된 이벤트나 matcher, 설정의 잘못된 JSON | 업데이트, /hooks 확인, 주석과 끝 쉼표 제거 |
ParserError 또는 "unexpected token" | PowerShell 구문이 Git Bash로 전달되었거나 그 반대 | shell을 명시적으로 설정 |
| 훅 출력이 "does not start with {" | JSON 앞에 무언가가 출력됨. 대개 셸 프로필 | 프로필 출력을 끄고 로그는 stderr로 보내기 |
\와 /가 섞인 경로를 찾을 수 없음 | Windows 경로 변수에 Unix 스타일 접미사가 붙음 | exec 형식 사용: 프로그램은 command에, 각 인수는 args에 |
Git Bash에서 powershell: Permission denied | 2.1.283 및 2.1.304~2.1.305에서 간헐적으로 보고됨(#103910, 미해결) | "shell": "powershell"을 사용해 Git Bash를 거치지 않게 하기 |
| 가드 훅이 모든 것을 통과시킴 | 종료 코드 2 대신 1 사용, 또는 이전 버전의 페일 오픈(fail-open) 버그 | 종료 코드 2로 차단, 2.1.310 이상으로 업데이트 |
| 훅이 출력 없이 즉시 반환됨 | 셸이 기다리지 않는 GUI 서브시스템 프로그램 | 콘솔 프로그램을 사용하거나 프로세스가 끝날 때까지 대기 |
stdout에는 JSON만
Claude Code는 훅의 표준 출력을 응답으로 읽습니다. 문서의 규칙은 간단합니다. stdout에는 JSON 객체 외에 아무것도 출력하지 마세요. Windows에서는 이 규칙이 교묘하게 깨집니다. Git Bash와 일부 환경은 여전히 셸 프로필을 불러오는데, 프로필에서 무조건 실행되는 echo가 JSON 앞에 붙습니다. 그러면 훅이 터미널에서는 "작동"하지만 Claude Code 안에서는 실패합니다.
대신 파일이나 stderr에 로그를 남기고, 프로필은 대화형 셸에서만 출력하도록 조건을 거세요.
종료 코드: 2는 차단, 1은 차단하지 않음
종료 코드 0은 성공입니다. 종료 코드 2는 차단 오류로, 차단 가능한 이벤트에서 Claude Code는 작업을 중지하고 stderr를 표시합니다. 유효한 JSON 없이 종료 코드 1을 반환하면 차단하지 않는 오류이며, 작업은 계속 진행됩니다. 훅으로 정책을 강제하려면 종료 코드 2를 사용하세요.
모든 이벤트를 이 방식으로 차단할 수 있는 것은 아닙니다. Notification 훅의 종료 코드는 무시됩니다. 종료 코드 2로 끝나는 Stop 훅은 Claude가 계속 작업하게 만듭니다. PermissionRequest 훅은 종료 코드 2를 완전히 무시하며, JSON의 decision 객체로 결정합니다.
Claude Code 2.1.309에서 훅 프로그램이 코드 2로 종료되었을 때 PowerShell 훅이 차단하지 않던 문제가 수정되었습니다. PowerShell 가드가 무시되는 것 같다면 먼저 버전을 확인하세요.
업데이트할 가치가 있는 Windows 수정 사항
Anthropic의 변경 로그에는 Windows 훅 수정 내역이 길게 이어져 있습니다. 오래된 포럼 스레드를 설명해 주는 항목은 다음과 같습니다.
| 버전 | 수정 내용 |
|---|---|
| 2.1.47 | PreToolUse 및 PostToolUse 훅이 Windows에서 오류 없이 실패함. 이제 cmd.exe 대신 Git Bash로 실행 |
| 2.1.161 | bash를 명시적으로 호출하는 훅(/usr/bin/bash script.sh)이 "command not found"로 실패함 |
| 2.1.299 | shell이 설정되지 않은 PreToolUse 및 PermissionRequest 훅이 Git for Windows가 없으면 페일 오픈되어 통과시킴. 이제 실패한 가드는 차단함 |
| 2.1.309 | PowerShell 훅이 종료 코드 2에서 차단하지 않음. "shell": "powershell" 가드가 Git for Windows가 없으면 페일 오픈되어 통과시킴 |
| 2.1.310 | 명령이 따옴표로 묶인 문자열만으로 이루어진 가드(예: 따옴표로 묶인 경로)가 Git Bash가 없으면 페일 오픈되어 통과시킴 |
"No hook is working on Windows"라는 제목의 예전 이슈(#10450)는 2.0.27에서 보고되었으며, 한 댓글 작성자가 stdin이 훅 명령에 전달되지 않는 것이 원인임을 밝혀냈습니다. 그렇게 오래된 버전을 쓰고 있다면 다른 것을 디버깅하기 전에 먼저 업데이트하세요.
프로그램을 직접 호출하기
훅이 컴파일된 프로그램이라면 exec 형식(args 필드, args가 설정되면 shell은 무시됨)으로 셸을 건너뛸 수 있습니다. Windows에서 exec 형식은 command가 .exe 같은 실제 실행 파일로 해석되어야 합니다. .cmd와 .bat 심(npm이 설치하는 대부분의 항목 포함)은 셸 없이 실행할 수 없습니다. .exe를 가리키거나 셸 형식을 사용하세요.
자체 훅 도우미를 만들면서 얻은 교훈이 하나 있습니다. Windows에서는 셸이 GUI 서브시스템용으로 빌드된 프로그램을 시작할 때 그 프로그램을 기다리지 않습니다. 셸은 즉시 반환되고, 에이전트는 빈 출력을 읽으며, 훅은 죽은 것처럼 보입니다. 훅 프로그램은 반드시 콘솔 프로그램이어야 합니다.
플러그인 훅과 경로
${CLAUDE_PLUGIN_ROOT}/hooks/run.sh 같은 경로를 만드는 플러그인 훅은 v2.1.2에서 v2.1.12 사이에 Windows에서 동작하지 않았습니다(#18610). 변수가 백슬래시 경로로 확장되고, 여기에 슬래시 접미사를 붙이면 bash가 해석할 수 없는 혼합 경로가 됩니다. 현재 문서는 깔끔한 해결책을 제시합니다. 훅이 경로 자리 표시자를 참조할 때는 항상 exec 형식을 사용하세요. 프로그램은 command에, 각 인수는 args에 넣습니다. 예: "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js"]. 각 요소는 하나의 인수로 전달되며, 이를 망가뜨릴 셸이 없습니다.
디버깅 체크리스트
/hooks명령은 구성된 모든 훅과 그 출처를 보여 주는 읽기 전용 목록을 엽니다.claude --debug(또는 세션 중/debug)는 각 훅 실행과 그 출력, 종료 코드를 보여 줍니다.claude --debug-file <path>는 이를 파일에 기록합니다.- JSON을 검증하세요. 설정 파일은 엄격한 JSON이며, 끝 쉼표 하나만 있어도 파일 전체가 비활성화됩니다.
- matcher를 확인하세요. matcher는 대소문자를 구분합니다.
"*",""또는 matcher가 없으면 모든 항목과 일치합니다. - 명령을 직접 실행해 보세요. Claude Code가 사용할 것과 같은 셸에서 샘플 JSON을 파이프로 넣어 실행합니다.
- 타임아웃을 확인하세요. 명령 훅의 기본값은 600초입니다. 사람의 응답을 기다리는 훅에는 그만큼의 여유가 필요합니다.
조사하는 동안 모든 훅을 끄려면 "disableAllHooks": true를 설정하세요.
알림과 승인만 원했다면
많은 Windows 훅 설정은 한 가지 이유로 존재합니다. Claude Code가 언제 나를 필요로 하는지 알고 응답하기 위해서입니다. Pet Island는 스크립트 없이 이를 해결합니다. 클릭 한 번으로 Claude Code에 연결되고, Windows의 세부 사항을 대신 처리하며, 모든 권한 요청, 질문, 계획을 명령이나 diff와 함께 화면 상단의 작은 아일랜드에 띄워 그곳에서 허용하거나 거부할 수 있게 합니다. Claude Code가 WSL 안에서 실행될 때도 작동합니다.
자주 묻는 질문
Windows에서 Claude Code 훅은 어떤 셸로 실행되나요?
Git Bash이며, Git Bash가 설치되어 있지 않으면 PowerShell입니다. 훅에 "shell": "powershell" 또는 "shell": "bash"를 설정해 명시적으로 선택하세요.
Claude Code 훅이 터미널에서는 작동하는데 Claude Code에서는 작동하지 않는 이유는 무엇인가요?
대개 불필요한 출력 때문입니다. 텍스트를 echo하는 셸 프로필의 출력이 훅의 JSON 앞에 붙습니다. stdout에는 JSON 객체만 출력하세요.
훅이 도구 호출을 차단하게 하려면 어떻게 하나요?
코드 2로 종료하세요(또는 JSON으로 deny 결정을 반환). 종료 코드 1은 차단하지 않는 오류이며 작업이 계속 진행됩니다. PermissionRequest 훅은 종료 코드 2를 무시하고 대신 decision 객체를 사용합니다.
훅 명령으로 .cmd나 .bat 파일을 사용할 수 있나요?
exec 형식에서는 안 됩니다. Windows에서는 .exe 같은 실제 실행 파일로 해석되어야 합니다. .cmd나 .bat 파일을 실행하려면 셸 형식을 사용하세요.
훅이 실패한 이유는 어떻게 확인하나요?
claude --debug를 실행하거나 /debug를 입력하고, /hooks로 어떤 훅이 로드되었는지 확인하세요.
출처
이 페이지의 모든 설정과 동작은 2026년 10월 26일에 아래 페이지와 대조해 확인했습니다. 에이전트는 빠르게 바뀌므로, 내용이 더 이상 맞지 않으면 공식 페이지를 따르세요.
- Claude Code 문서: Hooks reference(훅 레퍼런스)
- Claude Code 문서: Hooks guide(훅 가이드)
- Claude Code: CHANGELOG
- 이슈 #10450: Windows에서 어떤 훅도 작동하지 않음
- 이슈 #18610: 플러그인 훅이 Windows에서 스크립트를 실행하지 못함
- 이슈 #103910: 훅이 간헐적으로 powershell을 실행하지 못함(Git Bash)