Claude Code에서 echo hi도 실패할 때: 세션 환경 파일을 정리한 복구 기록

Claude Code의 Bash 오류와 세션 환경 파일 점검·복구 과정을 표현한 개념 표지

내 환경에서 Claude Code의 Bash 도구를 복구한 방법은 세션 환경 파일의 반복 내용을 정리한 뒤 앱을 다시 시작하는 것이었다. echo hi조차 실패하던 상태에서 첫 확인 명령이 다시 실행됐다.

다만 반복된 내용이 구문 오류의 직접 원인이었는지는 확인하지 못했다. 파일에는 같은 export 3줄이 27번, 총 81줄 들어 있었지만, 정상적인 대입문을 같은 횟수만큼 반복한 별도 시험에서는 오류가 나지 않았다. 복구에 성공한 순서와 원인 확인의 한계를 구분해 남긴다.

파일 정리 뒤 재시작하자 복구됐다

파일 정리와 재시작을 함께 했을 때 명령이 돌아왔다. 프로필 확인과 스냅샷 격리, 재시작만으로는 해결되지 않았다. 당시 시도별 결과는 다음과 같다.

시도 내 환경에서 나온 결과
프로필과 스냅샷 확인, 스냅샷 격리 같은 계열의 오류가 계속됐다
앱 재시작만 수행 오류 문구가 바뀌었지만 명령은 실패했다
세션 환경 파일의 반복 내용을 정리 재시작 전에는 오류가 계속됐다
정리 후 앱 재시작 첫 확인 명령부터 hi가 반환됐다
프로필과 스냅샷 확인, 스냅샷 격리
내 환경에서 나온 결과같은 계열의 오류가 계속됐다
앱 재시작만 수행
내 환경에서 나온 결과오류 문구가 바뀌었지만 명령은 실패했다
세션 환경 파일의 반복 내용을 정리
내 환경에서 나온 결과재시작 전에는 오류가 계속됐다
정리 후 앱 재시작
내 환경에서 나온 결과첫 확인 명령부터 hi가 반환됐다
$ echo hi
hi

이 경험 때문에 나는 해당 환경 파일을 중요한 점검 대상으로 본다. 그렇다고 파일이 언제 메모리에 읽히는지, 어느 단계에서 따옴표나 중괄호 해석이 깨졌는지까지 밝혀낸 것은 아니다. 내가 확인한 복구 조합은 ‘파일 정리 후 재시작’이었다.

명령을 바꿔도 같은 줄에서 실패했다

처음에는 가장 짧은 확인 명령도 실행되지 않았다. 배포를 앞두고 후보를 빌드하려던 때였고, git이나 ssh뿐 아니라 다음 명령도 실패했다.

echo hi

반환된 오류는 다음과 같았다.

처음 발생한 오류
/usr/bin/bash: -c: line 77: syntax error: unexpected end of file from `{' command on line 76
첨부 캡처에서 오류 문구만 옮겼다. 실제 제품 화면 캡처가 아닌 재현 패널이다.

내가 보낸 명령은 한 줄인데 Bash는 76~77줄을 가리켰다. 내가 입력한 내용 외에 실행 준비 코드가 함께 전달됐을 가능성을 먼저 살펴볼 만했다. 이 줄 번호만으로 어느 파일의 몇 번째 줄이 문제인지 알 수는 없었다.

서브에이전트에게 같은 명령을 맡겨도 실패했고, 입력창에서 ! echo hi로 직접 실행해도 같은 오류가 났다. 같은 PC에서 열어 둔 다른 작업 세션도 막혀 있었다. 당시에는 명령 하나의 문제보다 세션이나 공통 환경을 의심했다.

프로필 확인과 스냅샷 격리로는 해결되지 않았다

처음에는 ~/.bashrc와 ~/.bash_profile을 읽었다. 확인한 범위에서는 괄호나 따옴표가 명백히 깨진 부분을 찾지 못했다.

다음으로 ~/.claude/shell-snapshots/를 살폈다. 가장 최근 스냅샷은 210줄이 끝까지 저장돼 있었다. 당시에는 스냅샷 70개를 다른 폴더로 옮겨 보기도 했지만, 같은 오류가 계속됐다. 이 시도는 내 환경에서 효과가 없었으므로, 같은 증상을 만났다고 스냅샷부터 일괄 삭제하는 방법은 권하지 않는다.

앱을 다시 시작해도 해결되지 않았다. 이번에는 오류 문구가 달라졌다.

재시작만 했을 때의 오류
/usr/bin/bash: -c: line 76: unexpected EOF while looking for matching `''
첨부 캡처에서 오류 문구만 옮겼다. 실제 제품 화면 캡처가 아닌 재현 패널이다.

두 오류 모두 Bash가 명령 문자열을 해석하다가 끝을 만났다는 내용이었다. 다만 76줄과 77줄이 번갈아 나온 사실만으로 파일이 계속 커졌다고 판단할 수는 없다. 실행할 때 조합되는 앞부분이 달랐을 수도 있어 실제 파일 확인이 필요했다.

Claude Code와 Codex는 어떻게 연결될까

나는 Claude Code에서 Codex에 검토나 작업을 맡기는 플러그인을 쓰고 있었다. 플러그인은 같은 PC에 설치한 Codex CLI를 실행하고, Codex의 요청 처리 프로그램인 app-server와 통신한다. 이 연결 과정에서 쓰는 환경 설정도 셸 오류의 점검 대상이었다.

그림 1. Claude Code와 Codex를 잇는 실행 구조

사용자는 Claude Code에서 요청한다. 플러그인이 로컬 Codex 실행 경로를 연결한다.

내 PC에서 동작하는 프로그램

  1. Claude Code사용자 요청과 작업 문맥연결 모델: Claude
  2. Codex 플러그인명령·훅·Node.js 전달 스크립트작업 요청과 결과를 연결
  3. 로컬 Codex CLIcodex app-server로 작업 실행을 관리연결 모델: Codex 설정에 따름

작업 대상: 로컬 작업 폴더의 코드·변경사항 · 접근 권한은 실행 기능과 설정에 따름

오른쪽 방향: 작업 요청왼쪽 방향: 결과 반환위에서 아래: 요청 전달아래에서 위: 결과 반환

CLI와 app-server가 로컬에서 실행된다는 뜻이다. 모델 추론까지 PC 안에서 이뤄진다는 뜻은 아니며, 각 도구가 설정한 모델 서비스를 사용한다. 소스의 주요 역할을 묶은 개념도다. 구성 근거 · 그림 크게 보기

플러그인에서 어떤 명령을 선택하느냐에 따라 Codex가 하는 일도 달라진다. 코드 검토는 지적 사항을 돌려주고, 작업 위임은 요청한 작업을 맡긴다. 대화 이전은 지금까지의 문맥을 Codex에서 이어 쓰는 기능이다.

그림 2. 검토·작업 위임·대화 이전은 서로 다른 선택지

Claude Code의 Codex 플러그인 명령

코드 검토/codex:review

현재 변경사항이나 기준 브랜치와의 차이를 읽기 전용으로 검토한다.

돌아오는 것: 검토 결과

작업 위임/codex:rescue

요청한 작업을 Codex에 맡긴다. 수행 범위와 권한은 작업·설정에 따른다.

돌아오는 것: 작업 결과·상태

대화 이전/codex:transfer

현재 대화를 이어갈 Codex 스레드를 만들고, Codex에서 계속할 경로를 받는다.

돌아오는 것: 재개할 세션 정보

세 기능은 연속 실행 순서가 아니다. 아래 왕복 흐름은 이 중 코드 검토 한 건을 예로 든다. 기능별 설명 · 그림 크게 보기

검토 요청도 셸 실행 경로를 거친다

셸 오류는 일반 명령뿐 아니라 플러그인의 검토 요청에도 영향을 줄 수 있다. /codex:review가 Bash 도구로 Node.js 전달 스크립트를 실행하기 때문이다. 이 스크립트는 플러그인에서 요청과 결과를 전달하는 역할을 한다. Bash가 실행되지 않으면 검토 요청도 여기서 막힐 수 있다.

그림 3. 코드 검토 요청과 결과가 돌아오는 순서

예: 사용자가 /codex:review로 검토를 요청한 경우

요청 전달

  1. Claude CodeBash 도구로 전달 스크립트 실행
  2. 플러그인 런타임검토 대상과 옵션을 Codex 실행 경로로 전달
  3. Codex app-serverCodex가 검토 작업을 수행하도록 실행

결과 반환

  1. Codex 측 결과검토에서 찾은 내용 반환
  2. 플러그인 런타임검토 출력을 명령 결과로 반환
  3. Claude Code사용자에게 검토 결과 표시

백그라운드 실행을 선택했다면 /codex:status·/codex:result로 상태와 결과를 확인할 수 있다.

PC에서는 요청이 오른쪽으로, 결과가 왼쪽으로 진행한다. 모바일에서는 각 묶음을 위에서 아래로 읽는다. /codex:review는 코드를 수정하는 명령이 아니다. 실제 중간 통신 계층은 주요 역할로 묶어 표시했다. 검토 명령 근거 · 그림 크게 보기

CLAUDE_ENV_FILE도 명령 앞에서 실행된다

검토 요청이 셸을 거친다면, 명령 실행 전에 준비되는 환경도 확인해야 했다. Claude Code에서는 세션 시작 같은 특정 시점에 훅(hook)이라는 작업을 실행한다. SessionStart 훅은 이후 셸 명령에서 쓸 환경 변수를 CLAUDE_ENV_FILE에 저장할 수 있다.

공식 문서는 이 파일에 쓴 내용을 각 Bash 명령보다 먼저 셸 코드로 실행한다고 설명한다. SessionStart는 새 세션 시작뿐 아니라 세션 재개와 컨텍스트 압축 등에도 실행된다. 즉, 명령 앞에서 읽는 내용은 셸 프로필이나 스냅샷만이 아니다. Claude Code 훅과 환경 변수 문서

당시 내 세션에서 확인한 파일은 다음 경로에 있었다.

~/.claude/session-env/<세션 ID>/sessionstart-hook-1.sh

이 경로와 끝의 1은 그때 내 환경에서 본 값이다. 다른 설치에서도 파일명이 같다고 가정하면 안 된다.

파일에는 다음 세 변수의 설정이 반복돼 있었다. 아래 값은 실제 세션 ID와 개인 경로를 가린 예시다.

export CODEX_COMPANION_SESSION_ID='example-session'
export CODEX_COMPANION_TRANSCRIPT_PATH='C:/example/transcript.jsonl'
export CLAUDE_PLUGIN_DATA='C:/example/plugin-data'

이 세 줄이 27번 반복돼 총 81줄이었다. 나는 그 세션을 오래 이어 쓰면서 재시작과 재개, 컨텍스트 압축을 여러 번 거쳤다.

공개된 Codex 플러그인 소스에서도 세션 시작 처리에서 이 세 값을 환경 파일에 덧붙이는 코드를 확인할 수 있다. 이는 이번에 확인한 공개 소스의 동작이며, 당시 내 PC에 설치돼 있던 파일의 정확한 버전까지 대조한 것은 아니다. 확인한 커밋의 세션 훅 소스

명령을 실행하기 전에 읽는 환경

  1. SessionStart 훅환경 변수 설정을 파일에 기록
  2. 세션 환경 파일이 사례의 관찰: 3줄 × 27회 = 81줄
  3. Bash 명령환경 파일 내용을 먼저 실행
공식 문서의 환경 전달 관계를 정리한 그림이다. 81줄이라는 관찰과 EOF 오류의 직접 원인은 별도로 확인해야 한다.

같은 줄이 반복됐다는 사실만으로는 원인이 확정되지 않는다

정상적인 export 대입문 81줄만으로는 같은 오류가 재현되지 않았다. 실제 Claude 설정과 분리한 합성 파일에 3줄을 27번 넣고 마지막에 echo hi를 추가했다. Git Bash에서 문법 검사와 실행은 모두 종료 코드 0이었고, 실행 결과는 hi였다.

이 시험은 반복된 정상 대입문만으로 같은 구문 오류가 생기는지 확인한 것이다. Claude Code가 실제로 조합한 명령문이나 원래 문제가 난 파일을 재현한 시험은 아니다.

공개 저장소에는 같은 환경 변수가 반복해서 누적되는 보고가 있다. 긴 명령행이 한계에 걸렸다는 다른 사용자의 보고도 확인했다. 그 보고의 오류는 E2BIG이며, 내 사례의 unexpected EOF와 동일한 원인으로 확정할 수는 없다. 환경 파일 누적 관련 보고

또한 공식 문서는 다른 훅이 저장한 값을 보존하도록 덧붙이기(>>)를 사용하라고 설명한다. 따라서 덧붙이기 자체를 무조건 잘못된 구현이라고 볼 수도 없다. 같은 변수가 불필요하게 중복되는지, 값이 달라졌는지, 실행 직전 문자열이 잘렸는지를 나눠 확인해야 한다.

다시 겪는다면 이렇게 점검하겠다

우선 사용 중인 Claude Code와 플러그인 버전, 오류 전문, 실패한 명령을 기록하겠다. 이번 기록에는 당시 설치 버전이 없어 다른 환경과 정확히 비교하기 어렵다.

그다음 앱 안의 Bash 도구와 별도의 일반 터미널을 구분해서 확인한다. 앱 안에서만 실패한다면 프로필·스냅샷·세션 훅이 준비한 환경을 차례로 좁힐 근거가 된다. 어느 쪽에서나 실패한다면 Bash 설치나 사용자 셸 설정도 함께 조사해야 한다.

실제 대상 파일을 찾았다면 먼저 크기와 줄 수만 읽는다. 다음은 PowerShell의 읽기 전용 확인 예시다. <세션 ID>와 파일명은 내 환경에서 확인한 값으로 바꿔야 한다.

$sessionEnvPath = Join-Path $HOME '.claude\session-env\<세션 ID>\sessionstart-hook-1.sh'
Get-Item -LiteralPath $sessionEnvPath | Select-Object Length, LastWriteTime
(Get-Content -LiteralPath $sessionEnvPath).Count

내용을 정리해야 한다면 작업 중인 세션을 종료하고 대상 파일만 별도로 백업한 뒤 확인하겠다. 같은 변수라도 값이 달라졌을 수 있고 다른 훅의 설정이 섞여 있을 수 있으므로, 무조건 처음 3줄만 남기거나 폴더 전체를 지우면 안 된다. 값의 의미를 모르면 편집을 멈추고 새 세션에서 동작 차이를 확인하는 쪽이 낫다.

당시에는 도구에서 이 위치의 쓰기가 차단됐다는 기록도 있었다. 이를 모든 Claude Code 모드의 보호 정책으로 일반화하지는 않겠다. 권한이나 보호 장치를 우회하는 절차도 이 글에 넣지 않았다.

복구 후에는 echo hi부터 확인하고, 성공하면 저장소 상태 조회처럼 영향이 작은 명령으로 범위를 넓힌다. 나는 셸이 돌아온 뒤 멈춰 있던 빌드와 리허설을 이어 갔다.

이후에는 짧은 명령조차 같은 줄에서 실패하면, 명령 자체와 함께 그 앞에서 실행되는 환경도 확인한다. 파일이 커졌다는 관찰과 구문 오류의 직접 원인은 별도로 기록하고, 복구에 성공한 순서도 남겨 둔다.

함께 읽기

위로 스크롤