[태그:] Antigravity CLI

  • 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 실패가 같은 원인이라고 일반화하지 않는다.