brew upgrade 중 pyenv rehash 락 이슈

Python, zsh, bash

pyenv: cannot rehash: couldn't acquire lock /Users/zoey/.pyenv/shims/.pyenv-shim for 60 seconds. Last error message:
/opt/homebrew/Cellar/pyenv/2.6.26/libexec/pyenv-rehash: line 22: /Users/zoey/.pyenv/shims/.pyenv-shim: cannot overwrite existing file

평소처럼 맥에서 터미널을 열었음에도 오랜 시간이 지나야만 zsh이 켜지고, 그 전까지는 아무 응답 없는 bash shell만 올라온다.

Cellar

Cellar는 Homebrew가 패키지를 실제로 설치하는 디렉터리고, pyenvbrew install로 설치하면 실행 파일이 /opt/homebrew/Cellar/pyenv/2.6.26/처럼 패키지, 버전별로 디렉터리가 생성된다. /opt/homebrew/bin/pyenv는 이 곳으로 향하는 심볼릭 링크다. 에러 트레이스에 Cellar가 적힌 이유는 pyenv가 brew로 설치되었기 때문이었다.

원인

왜 이런 오류가 발생했을까? brew upgradepyenv를 업그레이드하면 post-install 단계에서 pyenv rehash가 돌아간다. rehash는 ~/.pyenv/shims/에 있는 심 스크립트들을 현재 설치된 Python 버전에 맞게 재생성하는 작업이다. 심(Shim)은 원래 명령어 앞에 끼어드는 얇은 중간 스크립트다. python을 실행하면 시스템의 python이 바로 실행되는 게 아니라, ~/.pyenv/shims/python이 먼저 실행돼서 ‘지금 어떤 버전을 쓸 지’ 판단한 뒤, 해당 버전의 python을 호출한다. 따라서 버전이 바뀌면 사용 가능한 실행 파일 목록도 달라지므로 심을 다시 만들어야 한다.

이때 시스템이 슬립에 들어가거나, 터미널을 닫거나, 프로세스가 kill되면 rehash가 중간에 끊기면서 락 파일을 해제하지 못한 채 남겨둔다. 이후 터미널을 열 때마다 .zshrceval "$(pyenv init -)"가 rehash를 시도하는데, stale 락 파일이 남아 있으니 60초간 획득을 재시도하다 실패한다. 쉘 초기화가 이 지점에서 블로킹되므로 그 전까지는 zsh 설정이 로드되지 않은 bare bash 상태로 보이는 것이었다.

아마도 동시에 다른 터미널에서 Claude Code, Python 서버를 구동하면서 pyenv init이 호출된 것으로 모이고, 두 프로세스가 동시에 rehash를 시도해서 한쪽이 락을 잡고, 다른 쪽은 대기하다가 업그레이드 중 바이너리 경로가 바뀌어 락을 잡은 쪽이 비정상 종료된 것으로 추측된다.

rehash의 락 방식에 대해서

pyenv-rehash 스크립트는 셸의 noclobber 옵션으로 .pyenv-shim 파일 생성을 시도하고, 이미 존재하면 실패하는 걸 뮤텍스처럼 사용한다. flock 같은 OS 레벨 락과 달리, 프로세스가 죽어도 자동 해제되지 않기 때문에 한 번 남으면 직접 지울 때까지 계속 막힌다.

해결

락 파일을 제거하면 이 이슈는 해결된다.

rm -f ~/.pyenv/shims/.pyenv-shim