[태그:] PowerShell

  • Claude Code가 설치는 됐는데 안 켜질 때 — Windows 첫 실행 오류 전부 정리

    Claude Code가 설치는 됐는데 안 켜질 때 — Windows 첫 실행 오류 전부 정리

    설치는 끝났다고 나왔는데 claude를 쳤을 때 아무 일도 일어나지 않는다면, 대부분은 프로그램이 망가진 것이 아니다. 막힌 층이 어디인지만 가려내면 처방은 거의 정해져 있다. 이 글은 Windows에서 처음 실행할 때 나오는 오류를 증상 문구별로 모아 정리한 것이다. 앞의 여섯 항목은 공식 문서에서 확인한 내용이고 링크를 함께 달았다. 뒤의 세 항목은 우리가 이 컴퓨터에서 직접 겪은 것이라 공식 문서에는 없다. 어느 쪽인지 항목마다 밝혀 두었다.

    설치는 됐는데 안 켜지는 상황을 네 층으로 나눈 도식. 설치 명령 층, 경로 층, 셸 세션 층, 정책과 인증 층의 대표 오류 문구와 처방을 나란히 놓고, 아래 띠에 우리가 직접 겪은 세 가지를 적었다.
    막히는 층은 네 곳이다 — 증상 문구로 층을 먼저 가려낸다. 아래 띠는 우리가 2026년 9월에 직접 겪은 세 가지다.

    어떤 문구가 떴는가

    지금 화면에 나온 문구와 가장 비슷한 항목으로 바로 가면 된다. 초보일 때는 원인을 몰라도 괜찮다. 증상만 알면 충분하다.

    1. claude를 쳤는데 명령을 찾을 수 없다고 나온다

    증상. 화면에 command not found: claude 또는 'claude' is not recognized가 나온다. 설치는 분명히 끝났는데 명령만 없는 것처럼 보인다.

    왜 그런가. 공식 문서는 이렇게 설명한다. 설치는 됐지만 설치 폴더가 PATH에 없다는 뜻이고, 셸은 PATH에 적힌 폴더만 뒤져서 프로그램을 찾는다. Windows에서 설치 파일이 놓이는 자리는 %USERPROFILE%\.local\bin\claude.exe다.

    확인 명령. PowerShell에서 그 폴더가 PATH에 들어 있는지 본다.

    
    $env:PATH -split ';' | Select-String '\.local\\bin'
    

    처방. 위 명령이 아무것도 출력하지 않으면 PATH에 없는 것이다. 공식 문서가 안내하는 대로 사용자 PATH에 추가한다.

    
    $currentPath = [Environment]::GetEnvironmentVariable('PATH', 'User')
    [Environment]::SetEnvironmentVariable('PATH', "$currentPath;$env:USERPROFILE\.local\bin", 'User')
    

    공식 문서는 여기에 한 줄을 덧붙인다. “Restart your terminal for the change to take effect.” 즉 창을 다시 열어야 반영된다.

    확인 방법. 새 터미널 창을 열고 claude --version을 실행한다. 공식 문서 기준으로 정상 설치라면 2.1.211 (Claude Code)처럼 버전 번호가 찍힌다.

    한 가지 주의할 점이 문서에 따로 적혀 있다. VS Code 확장만 설치한 경우에는 claude가 그 자리에 놓이지 않는다. 확장은 자기 채팅 창을 위해 CLI 사본을 확장 폴더 안에 따로 두고 PATH에 추가하지 않기 때문이다. 터미널에서 claude를 쓰려면 별도 설치가 필요하다.

    2. 설치는 성공했다는데 새 창에서만 되거나, 옛 버전이 나온다

    증상. 설치 로그는 성공인데 방금 설치한 그 창에서만 claude를 못 찾는다. 또는 분명히 새로 설치했는데 옛 버전 번호가 찍힌다.

    왜 그런가. 공식 문서의 설명은 짧고 분명하다. “the session you installed from keeps its old PATH.” 설치 프로그램이 PATH를 바꿔도 이미 열려 있던 창은 예전 PATH를 그대로 들고 있다. 고객지원 문서도 Windows에서는 “Close and reopen PowerShell”이라고 안내한다.

    확인 명령. 지금 창에서 실패하는지, 새 창에서도 실패하는지를 나눠서 본다.

    
    claude --version
    

    처방. 창을 완전히 닫고 새 창을 열어 같은 명령을 다시 실행한다. 이것만으로 끝나는 경우가 많다.

    확인 방법. 새 창에서 버전 번호가 찍히면 해결된 것이다. 새 창에서도 여전히 못 찾으면 1번 항목의 PATH 문제로 넘어간다.

    3. 설치 명령 자체가 오류를 낸다

    증상. Claude Code를 켜기도 전에 설치 명령이 튕긴다. 공식 문서가 열거한 문구는 이렇다.

    
    'bash' is not recognized as the name of a cmdlet
    A parameter cannot be found that matches parameter name 'fsSL'
    The token '&&' is not valid
    'irm' is not recognized
    

    왜 그런가. 공식 문서는 이 네 가지를 모두 같은 원인으로 묶는다. 다른 셸이나 다른 운영체제용 설치 명령을 복사해 온 것이다. 예를 들어 curl -fsSL ... | bash는 macOS와 Linux용인데, Windows PowerShell에서는 curlInvoke-WebRequest의 별칭이라서 -fsSL 같은 옵션을 거부한다.

    확인 명령. 내가 지금 어느 셸에 있는지부터 확인한다. 공식 문서가 알려주는 구분법은 프롬프트 모양이다. “Your prompt shows PS C:\ when you’re in PowerShell and C:\ without the PS when you’re in CMD.”

    처방. 셸에 맞는 설치 명령을 쓴다. 공식 문서에 적힌 그대로다.

    
    PowerShell:  irm https://claude.ai/install.ps1 | iex
    CMD:         curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
    WinGet:      winget install Anthropic.ClaudeCode
    

    설치 명령을 쳤는데 스크립트 내용만 화면에 쭉 출력되고 아무것도 설치되지 않는 경우도 있다. 공식 문서는 이것을 명령의 앞부분만 실행한 상태로 설명한다. irm https://claude.ai/install.ps1만 쓰면 내려받은 스크립트를 화면에 인쇄하고 끝난다. 실행까지 하려면 | iex를 붙여야 한다.

    확인 방법. 새 터미널을 열고 claude --version으로 버전 번호를 확인한다.

    4. Claude Code does not support 32-bit Windows가 나온다

    증상. 64비트 컴퓨터를 쓰는데도 32비트를 지원하지 않는다는 오류가 나온다.

    왜 그런가. 공식 문서의 설명은 이렇다. 시작 메뉴에는 PowerShell 항목이 두 개 있다. Windows PowerShellWindows PowerShell (x86)이다. x86 항목은 32비트 프로세스로 실행되며, 문서 표현대로 “triggers this error even on a 64-bit machine”이다. 즉 컴퓨터가 아니라 창을 잘못 연 것이다.

    확인 명령. 오류가 난 바로 그 창에서 실행한다.

    
    [Environment]::Is64BitOperatingSystem
    

    처방. True가 나오면 운영체제는 문제가 없다. 그 창을 닫고 (x86)이 붙지 않은 Windows PowerShell을 열어 설치 명령을 다시 실행한다. False가 나오면 32비트 Windows를 쓰는 것이고, 이 경우에는 64비트 운영체제가 필요하다. 공식 시스템 요건은 Windows 10 1809 이상 또는 Windows Server 2019 이상, 4GB 이상 메모리, x64 또는 ARM64 프로세서다.

    확인 방법. x86이 아닌 창에서 설치한 뒤 claude --version으로 버전 번호를 확인한다.

    5. running scripts is disabled on this system이 나온다

    증상. npm으로 설치했거나 npm으로 설치한 claude를 실행할 때 보안 오류가 난다. 공식 문서가 실은 원문은 이렇다.

    
    npm : File C:\Program Files\nodejs\npm.ps1 cannot be loaded because running scripts
    is disabled on this system.
        + CategoryInfo          : SecurityError: (:) [], PSSecurityException
    

    원문 메시지 끝에는 마이크로소프트의 실행 정책 문서를 가리키는 주소가 한 줄 더 붙는다. 위 인용에서는 그 주소만 덜어냈고, 같은 문서를 이 글 맨 아래 출처에 링크해 두었다.

    왜 그런가. 공식 문서는 원인을 이렇게 짚는다. PowerShell의 실행 정책이 npm이 만들어 놓은 .ps1 실행 스크립트를 막고 있는 것이다. 같은 오류가 claude.ps1 이름으로도 나온다. 문서는 이 정책이 스크립트 파일에만 적용되므로 PowerShell 설치 명령 irm https://claude.ai/install.ps1 | iex에는 영향을 주지 않는다고 덧붙인다. 내려받은 글자를 바로 실행하는 방식이라 그렇다.

    실행 정책이 무엇인지는 마이크로소프트 공식 문서에 정의돼 있다. 문서는 실행 정책을 “a safety feature that controls the conditions under which PowerShell loads configuration files and runs scripts”라고 설명한다. 이 글에서 쓰는 RemoteSigned는 문서 표현으로 “Requires a digital signature from a trusted publisher on scripts and configuration files that are downloaded from the internet”이고 “Doesn’t require digital signatures on scripts that are written on the local computer and not downloaded from the internet”이다. 즉 인터넷에서 받은 스크립트만 서명을 요구하고, 내 컴퓨터에서 만들어진 스크립트는 그대로 실행한다.

    한 가지 헷갈리기 쉬운 대목이 있어 그대로 옮긴다. 같은 문서는 RemoteSigned를 “The default execution policy for Windows computers”라고 적으면서, 동시에 “If no execution policy is set in any scope, the effective execution policy is Restricted, which is the default for Windows clients”라고도 적는다. 두 문장이 함께 있으니, 내 컴퓨터가 지금 어떤 상태인지는 추측하지 말고 직접 확인하는 편이 정확하다.

    확인 명령. 현재 적용 중인 정책을 범위별로 본다.

    
    Get-ExecutionPolicy -List
    

    처방. 공식 문서가 제시하는 방법은 세 가지다. 첫째, 내 계정 범위에서 로컬 스크립트를 허용한다.

    
    Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
    

    둘째, .ps1 대신 .cmd 실행 파일을 부른다. 문서 설명대로 npm.cmdclaude.cmd는 같은 일을 하고 정책 적용 대상이 아니다. 셋째, npm 대신 PowerShell 설치 명령을 쓴다. 스크립트가 아니라 실행 파일을 설치하는 방식이다. 마이크로소프트 문서는 Set-ExecutionPolicy의 변경이 즉시 적용되며 PowerShell을 다시 시작할 필요가 없다고 적는다.

    확인 방법. Get-ExecutionPolicy -Scope CurrentUserRemoteSigned를 돌려주는지 보고, 이어서 claude --version으로 버전 번호를 확인한다.

    6. 켜지기는 하는데 로그인이 안 된다

    증상. claude는 켜지는데 로그인 단계에서 멈춘다. 브라우저가 저절로 열리지 않거나, OAuth error: Invalid code가 나오거나, 로그인 뒤에 403 Forbidden이 나온다.

    먼저 확인할 것. 공식 문서는 계정 조건을 명확히 적어 두었다. Claude Code는 Pro, Max, Team, Enterprise, Console 계정이 필요하고 “The free Claude.ai plan does not include Claude Code access.” 무료 요금제로는 접근이 되지 않으니, 설치 문제로 오래 헤매기 전에 계정 종류부터 확인하는 편이 빠르다.

    확인 명령. Claude Code 안에서 현재 어떤 인증 방식이 쓰이는지 본다.

    
    /status
    

    처방. 공식 문서가 안내하는 순서는 이렇다. 원인이 분명하지 않을 때는 로그인을 깨끗하게 다시 한다. /logout으로 완전히 로그아웃하고, Claude Code를 닫고, claude로 다시 시작해 인증을 처음부터 진행한다. 브라우저가 저절로 열리지 않으면 c를 눌러 인증 주소를 복사한 뒤 브라우저에 직접 붙여 넣는다. 터미널이 좁아 주소가 여러 줄로 잘려 클릭이 안 될 때도 같은 방법을 쓴다.

    OAuth error: Invalid code. Please make sure the full code was copied가 나오는 경우, 문서는 코드가 만료됐거나 복사 과정에서 잘렸다고 설명한다. 브라우저가 열린 뒤 지체 없이 로그인을 마치는 것이 처방이다.

    로그인은 됐는데 403 Forbidden이 나오는 경우에는 환경 변수를 본다. 문서 설명대로 ANTHROPIC_API_KEY가 설정돼 있고 그 키를 승인해 둔 상태라면, Claude Code는 구독 계정의 인증 대신 그 키를 쓴다. Windows에서는 PowerShell 프로필($PROFILE)과 사용자 환경 변수에서 ANTHROPIC_API_KEY를 확인하라고 안내한다.

    확인 방법. /status로 지금 활성화된 인증 방식이 의도한 것과 같은지 확인한다.

    7. 점검 도구가 한글 콘솔에서 통째로 멈춘다

    여기서부터 세 항목은 공식 문서에 없다. 우리가 이 컴퓨터에서 직접 겪은 것이다.

    증상. 설치 상태를 점검하려고 돌린 진단 스크립트가 아래 오류를 내고 그 자리에서 죽었다. 점검 결과를 한 줄도 못 봤다.

    
    UnicodeEncodeError: 'cp949' codec can't encode character '\u2014'
    

    무엇이 문제였나. 정확히 밝혀 둔다. 죽은 것은 Claude Code 본체가 아니라 우리가 만든 파이썬 진단 스크립트였다. 원인은 프로그램 논리가 아니라 글자였다. 한국어 Windows 콘솔의 기본 인코딩은 cp949이고, 여기에는 줄표(U+2014, em dash)를 표현할 자리가 없다. 진단 결과를 출력하는 문장에 줄표가 한 글자 들어 있었고, 그 한 글자를 인코딩하는 순간 예외가 나면서 진단이 통째로 중단됐다. 설치 상태는 아무 문제가 없었다.

    확인 명령. 지금 파이썬이 어떤 인코딩으로 출력하는지 본다.

    
    python -c "import sys; print(sys.stdout.encoding)"
    

    처방. 우리가 실제로 쓴 방법은 출력 인코딩을 UTF-8로 고정하는 것이었다. 파이썬은 환경 변수로 이것을 지정할 수 있다.

    
    $env:PYTHONUTF8 = "1"
    $env:PYTHONIOENCODING = "utf-8"
    

    덧붙이자면, 겪고 나서 우리가 코드에 남긴 교훈은 다른 쪽이었다. 진단 도구의 출력 문장은 예쁜 기호를 쓰지 않는 편이 낫다는 것이다. 줄표 대신 붙임표를 쓰면 이 고장 자체가 생기지 않는다. 도구가 멈추면 정작 확인하려던 사실을 아무것도 못 보게 된다.

    확인 방법. 같은 스크립트를 다시 돌려 끝까지 출력이 나오는지 본다. 위 확인 명령이 utf-8을 돌려주면 인코딩은 잡힌 것이다.

    8. 분명히 설치했는데 자동 점검이 미설치라고 한다

    증상. 파일 탐색기로 확인하면 실행 파일이 그 자리에 분명히 있다. 그런데 우리가 만든 자동 점검이 그 CLI를 미설치로 판정했다.

    무엇이 문제였나. 실행 파일은 실재했고, 문제는 점검을 수행한 쪽의 PATH였다. 우리 자동 점검은 별도 프로세스를 띄워서 명령을 찾는데, 그 프로세스가 물려받은 PATH에 해당 폴더가 들어 있지 않았다. 그래서 파일이 있는데도 없다고 보고했다. 사람이 직접 창을 열어 실행하면 되는데 자동 점검만 실패하는 상태였고, 우리는 이 차이를 알아채기까지 시간을 썼다.

    이 오판정은 1번 항목과 뿌리가 같다. 셸이든 자동화든 프로그램을 찾는 방법은 PATH뿐이라, PATH를 물려받지 못한 프로세스에게는 그 파일이 존재하지 않는 것과 같다. 다른 점은 하나다. 1번은 사람이 오류 문구를 보고, 8번은 자동화가 조용히 잘못된 결론을 남긴다.

    확인 명령. 지금 이 창에서 찾을 수 있는지, 그리고 파일이 실재하는지를 따로 확인한다. 둘의 답이 갈리면 PATH 문제다.

    
    where.exe claude
    Test-Path "$env:USERPROFILE\.local\bin\claude.exe"
    

    처방. 파일은 있는데 where.exe가 못 찾으면 1번 항목의 PATH 추가를 적용한다. 자동화 쪽이라면 자동화가 띄우는 프로세스의 PATH에 그 폴더가 들어가는지를 따로 확인해야 한다. 사람이 쓰는 창에서만 고쳐 놓으면 자동 점검은 계속 미설치라고 답한다.

    확인 방법. where.exe claude가 경로를 출력하고, 자동 점검을 다시 돌렸을 때 같은 결론이 나오는지 본다. 사람과 자동화의 답이 일치하는 상태가 정상이다.

    9. 어제까지 되던 명령이 셸을 바꾸니 사라졌다

    증상. 잘 쓰던 CLI가 어느 날 없는 명령이 됐다. 우리 경우에는 셸을 바꿔서 새로 열었을 때였다.

    무엇이 문제였나. 정확히 적어 둔다. 이때 사라진 것은 Claude Code가 아니라, 같은 컴퓨터에 npm 전역 설치로 넣어 둔 다른 CLI였다. 그 컴퓨터에는 Node 버전 관리자(fnm)가 깔려 있었고, npm 전역 설치는 버전 관리자가 셸마다 만들어 주는 경로 아래로 들어갔다. 그 경로는 셸 세션에 딸린 임시 경로여서, 셸이 바뀌자 그대로 사라졌다. 파일이 지워진 것이 아니라 그 경로를 아무도 안 보게 된 것이다.

    처방. 우리가 쓴 해법은 셸에 딸리지 않는 안정된 절대경로에 실행 지점을 고정하는 것이었다. 구체적으로는 ~/.local/bin 아래에 작은 실행 파일을 두고 그것을 부르게 했다. 그 뒤로는 셸이 바뀌어도 같은 자리에서 실행된다.

    Claude Code에는 이 문제가 애초에 덜 생긴다. 공식 문서에 따르면 기본 설치 방식은 %USERPROFILE%\.local\bin\claude.exe라는 고정된 자리에 실행 파일을 놓기 때문이다. npm 전역 설치를 택할 때만 위와 같은 상황을 고려하면 된다. 참고로 공식 문서는 npm 설치에 대해 “Do NOT use sudo npm install -g“라고 경고하고, npm 패키지도 결국 같은 네이티브 실행 파일을 설치한다고 설명한다.

    확인 방법. 셸을 바꿔 새 창을 열고 where.exe claude가 같은 경로를 출력하는지 본다. 셸에 따라 결과가 달라지면 아직 휘발성 경로에 물려 있는 것이다.

    정리

    아홉 가지를 봤지만 실제로 층은 네 개다. 설치 명령을 셸에 맞게 골랐는가, 설치 폴더가 PATH에 있는가, 지금 창이 새 PATH를 들고 있는가, 정책과 계정이 막고 있지는 않은가. 이 네 가지를 순서대로 짚으면 대부분 끝난다.

    마지막으로 점검 명령 하나를 적어 둔다. 공식 문서는 설치와 설정을 자세히 보려면 claude doctor를 실행하라고 안내한다. 문서 설명으로는 세션을 시작하지 않고 읽기 전용 진단만 출력하며, 설치 상태와 설정 파일 오류, 권장 조치가 함께 나온다.

    
    claude doctor
    

    우리가 겪은 세 가지에서 남은 교훈은 하나로 모인다. 프로그램이 없다는 메시지는 대체로 프로그램이 없다는 뜻이 아니고, 그것을 찾는 쪽이 어디를 보는지의 문제였다. 그리고 점검 도구는 자기가 먼저 멈추지 않아야 쓸모가 있다.

    출처

    7번부터 9번까지는 2026년 9월에 우리 환경에서 직접 확인한 것이며 공식 문서의 내용이 아니다. 나머지 항목의 문구와 명령은 위 출처에서 인용했다.

  • Cannot run a document in the middle of a pipeline — PowerShell이 확장자 없는 CLI를 거부한 사고 기록

    Cannot run a document in the middle of a pipeline — PowerShell이 확장자 없는 CLI를 거부한 사고 기록

    2026년 8월 31일, 나는 Windows 11에서 cys 멀티에이전트 워크스페이스를 기동했다. 여러 노드 가운데 reviewer-gemini만 살아나지 않았다. 이 노드는 Antigravity CLI를 쓰며 실행 명령은 agy다. 아래 내용은 당시 화면, 설정 파일과 명령 결과를 따라가며 남긴 사고 기록이다. 원인을 안 뒤에 그럴듯하게 재구성한 튜토리얼이 아니다. 우리 AI 팀에서 한 노드가 무엇을 잘못 읽었고, 다른 노드의 반증이 어떻게 진단을 뒤집었는지를 순서대로 적는다.

    1. 증상

    reviewer-gemini의 pane에는 실행 명령이 에코됐다. 그 직후 PowerShell 프롬프트가 다시 나타났다. 오래 기다리다 죽은 것도 아니고, 오류 메시지를 남긴 것도 아니었다. 프로세스가 잠깐 떴다가 종료된 것처럼 보이기 쉬운 화면이었다.

    먼저 우리 팀은 cli.log를 확인했다. 그날 기록은 0줄이었다. 이 숫자가 중요했다. 보통 CLI가 초기화된 뒤 인증이나 API 호출에서 실패했다면 적어도 초기화 과정의 흔적을 기대할 수 있다. 여기에는 아무것도 없었다. 당시에는 이 침묵을 충분히 무겁게 보지 않았다.

    재현 과정에서 AGYRC=0도 보였다. 종료코드가 0이면 정상 종료라고 읽기 쉽다. 그러나 그 값이 방금 실행하려던 agy의 종료코드라는 보장은 없었다. PowerShell 프롬프트로 즉시 돌아온 화면, 0줄짜리 로그, 출처가 확인되지 않은 종료코드가 함께 있었지만 처음에는 각각 따로 보았다.

    2. 오진

    첫 번째 진단은 OAuth 만료였다. 우리 AI 팀의 CSO 노드가 토큰 JSON을 읽었고 token.expiry 값이 2026-08-18인 것을 확인했다. 장애일은 2026년 8월 31일이므로 날짜만 놓고 보면 이미 만료됐다. 판독 자체는 맞았다. 문제는 그 값에서 곧바로 “OAuth가 만료돼 CLI가 기동하지 못했다”는 인과관계까지 확정한 데 있었다.

    두 번째 오진은 AGYRC=0을 정상 종료의 증거로 받아들인 것이다. 실제로는 agy 프로세스가 시작되지 않았고 $LASTEXITCODE에는 직전 명령이 남긴 값이 들어 있었다. 실행 여부를 확인하지 않은 채 종료코드만 읽으니 “실행은 됐고 정상적으로 끝났다”는 이상한 설명이 만들어졌다. 오류도 로그도 없다는 사실이 이 설명을 의심하게 하기보다, 조용히 끝난 프로그램이라는 해석을 강화했다.

    조사 셸도 문제였다. 조사 노드는 Git Bash에서 agy를 시험했고 거기서는 실행됐다. 같은 Windows 기계에서 같은 경로의 같은 파일을 확인했으니 실행 파일은 정상이라는 결론으로 기울었다. 하지만 장애가 난 pane은 PowerShell이었다. “같은 기계”가 “같은 실행 조건”을 뜻하지 않았는데 셸 차이를 변수에서 빼버렸다.

    같은 파일을 PowerShell 과 Git Bash 에서 실행한 결과 비교. PowerShell 은 Cannot run a document in the middle of a pipeline 오류로 거부하고, Git Bash 는 모델 목록을 정상 출력한다.
    확장자 없는 같은 실행 파일을 두 셸에서 실행한 실제 결과 (2026-08-31 실측). PowerShell 만 거부한다.

    3. 반증

    CSO의 OAuth 가설은 다른 노드가 실제 호출로 확인했다. 먼저 다음 명령을 실행했다.

    
    agy models
    

    모델 목록이 정상 출력됐다. 이어 agy -p로 실제 API 호출을 했고 응답을 받았다. 로컬 도움말이나 캐시만 열린 것이 아니라 인증이 필요한 요청까지 성공한 셈이다. token.expiry2026-08-18이라는 판독과 현재 인증이 작동한다는 사실은 동시에 참이었다. refresh_token이 있는 상태에서는 만료 시각 하나만으로 현재 인증 실패를 단정할 수 없었다.

    다음은 실행 경로를 셸별로 갈라 확인했다. Git Bash에서 성공한 결과를 PowerShell pane의 증거로 재사용하지 않고, 장애가 발생한 바로 그 PowerShell에서 같은 대상 파일을 실행했다. 그때 드러난 실제 오류 문자열은 다음과 같았다.

    
    Cannot run a document in the middle of a pipeline
    

    이 메시지가 나온 뒤에야 0줄짜리 cli.log와 즉시 돌아온 프롬프트가 한 줄로 이어졌다. 인증 단계에서 죽은 것이 아니라 PowerShell이 대상을 실행 파일로 취급하지 않아 프로그램 초기화 이전에 멈춘 것이었다. AGYRC=0 역시 해당 프로세스가 남긴 값이 아니므로 반증 자료에서 제외했다.

    독자가 같은 상황을 확인하려면 성공한 셸 하나에서 결론내리지 말고 문제의 pane과 동일한 셸에서 검사해야 한다. PowerShell에서 Get-Command agy -All로 해석되는 경로를 확인하고 Get-Item ~/.local/bin/agy로 실제 파일 이름과 확장자를 본다. 그다음 PowerShell에서 해당 경로를 직접 실행하고, 실행 직후 생성된 로그의 행 수와 프로세스 존재 여부를 함께 확인한다. $LASTEXITCODE를 기록하려면 다른 명령을 사이에 끼우지 말고, 먼저 대상 프로세스가 실제 시작됐다는 증거를 확보해야 한다. 같은 파일을 Git Bash에서도 실행해 결과가 갈리면 인증보다 셸의 실행 파일 판정 규칙을 먼저 비교할 수 있다.

    4. 진짜 원인

    ~/.local/bin/agy는 154MB 파일이었지만 확장자가 없었다. Git Bash는 이 파일을 실행했다. PowerShell은 확장자 없는 파일을 실행 파일로 인정하지 않았고, 해당 pane에서 agy는 프로세스로 시작되지 않았다. 같은 기계와 같은 경로에서도 셸이 달라지자 결과가 갈린 것이다.

    나는 확인된 원인에 맞춰 세 가지를 조치했다. 먼저 원본과 같은 데이터를 가리키는 agy.exe 하드링크를 만들었다. 파일 크기는 154MB로 보이지만 하드링크이므로 디스크를 154MB 더 쓰지 않는다. 다음으로 cys 설정의 실행 명령을 agy에서 agy.exe로 바꿨다. 마지막으로 죽은 좌석을 새 좌석으로 교체하지 않고 기존 좌석에서 직접 기동했다. 그래야 reviewer-gemini의 역할 등록을 그대로 유지할 수 있었다.

    변경 뒤 reviewer-gemini는 Gemini 3.7 Flash로 정상 각성했다. 이 결과는 OAuth 토큰을 새로 발급하거나 좌석을 재등록해서 얻은 것이 아니다. PowerShell이 실행할 수 있는 이름을 제공하고 기존 좌석에서 다시 시작한 결과였다.

    5. 재발 방지

    이번 사고에서 고친 것은 파일명 하나지만, 재발 방지 기준은 실행 환경 전체를 향한다. 확장자 없는 CLI를 Windows에 배치할 때에는 Git Bash에서 한 번 실행해 끝내지 않는다. 실제 운영 pane이 PowerShell이면 PowerShell에서도 실행하고, 반대 방향도 확인한다. 크로스셸 지원을 주장하려면 각 셸의 결과가 따로 있어야 한다.

    종료코드는 실행 증거와 묶어 읽는다. $LASTEXITCODE가 0이라는 사실보다 먼저 확인할 것은 “방금 그 프로세스가 실제로 시작됐는가”다. 새 로그 행, 프로세스 관찰, 해당 명령이 직접 만든 출력 가운데 하나도 없다면 잔여 종료코드일 가능성을 버리지 않는다.

    로그 0줄도 실패 정보다. “오류를 기록하지 못한 실패”라고 곧바로 해석하지 않고, 로거가 초기화되기 전인 미실행 단계부터 확인한다. 인증 만료일처럼 눈에 잘 띄는 값은 원인이 아니라 가설의 출발점으로만 쓴다. 이번에는 token.expiry 판독은 정확했지만 agy modelsagy -p가 그 인과 추론을 무너뜨렸다.

    무엇보다 재현 환경을 장애 환경과 맞춘다. 같은 기계, 같은 파일이라는 두 조건만으로는 부족했다. 셸까지 같아야 했다. 앞으로 reviewer 노드가 말없이 프롬프트로 돌아오면 인증을 손대기 전에 실행 파일 이름, 확장자, 셸의 명령 해석, 프로세스 시작 여부, 로그 첫 행 생성 여부를 이 순서로 확인한다.

    이 기록의 날짜, 화면 증상, 로그 0줄, 토큰 만료 필드, 명령 결과, 파일 크기, 셸별 차이, 조치와 복구 결과는 2026년 8월 31일 우리 팀이 남긴 1차 사고 기록을 근거로 한다. 이 한 건에서 확인한 범위만 적었으며, 모든 OAuth 만료나 모든 PowerShell CLI 실패가 같은 원인이라고 일반화하지 않는다.