당신이 쓰는 CLI 도구는 자격증명을 어디에 둡니까? 한 군데라고 확신하십니까?
저는 이 질문을 3주 동안 안 했습니다. 대신 에러 메시지를 읽었습니다.
Failed to authenticate: OAuth session expired and could not be refreshed무인 스케줄 잡이 이 문장으로 죽었습니다. 하루 이틀이 아니라 17일 동안 일곱 번. 문장이 만료라고 하니 저는 재로그인을 했습니다. 다음 날 또 죽었습니다.
세 번 기각된 가설
이 로그를 쫓으면서 가설을 세 번 세웠습니다.
1차 — 단순 만료. 재로그인하면 며칠 가야 하는데, 재로그인한 당일 저녁에 다시 죽었습니다. 기각.
2차 — 무인 환경. 무인 시각에만 난다면 TTY가 없어서일까. env -i로 환경변수를 전부 비우고 TTY 없이 호출해 봤더니 잘 됐습니다. 기각.
3차 — 낡은 값 되쓰기. 실패 순간의 자격증명 파일을 찍는 계측을 붙였더니, 토큰이 8시간 남은 채로 인증에 실패하고 있었습니다. 만료는 확실히 아니었습니다. 그때 저는 오래 살아 있는 대화 세션이 자기 메모리의 낡은 값을 파일에 되쓴다고 결론 냈습니다.
그것도 정확히는 틀렸습니다. 그건 정황이었습니다. 되쓰기는 실제로 일어나고 있었지만, 왜 그게 무인 잡만 죽이는지는 설명하지 못했습니다.
가른 것은 대조 한 번이었습니다
오늘 아침, 계측 원장에 빈 토큰이 기록되는 순간을 실시간으로 잡았습니다. 자격증명 파일을 열었습니다.
accessToken: "" refreshToken: "" expiresAt: 0그 다음에 한 번도 안 해본 걸 했습니다. 같은 순간에 시스템 키체인을 열어봤습니다.
accessToken: 108자 expiresAt: 지금부터 7시간 58분 뒤한쪽은 비었고 한쪽은 멀쩡했습니다. 같은 계정, 같은 도구, 같은 초.
여기서 잠깐 — 당신이라면 다음에 뭘 하시겠습니까? 저는 두 갈래를 봤습니다. 키체인 값을 파일에 복사해보거나, 아니면 문서를 더 읽거나. 복사가 30초 걸리고 답을 확정하니 그걸 했습니다.
빈 파일 상태로 무인 호출:
$ env -i HOME=... PATH=... claude -p 'OK만 답해'
Failed to authenticate: OAuth session expired and could not be refreshed
rc=1사고 문구가 그대로 재현됐습니다. 키체인 값을 파일에 써넣고 같은 명령:
OK
rc=0즉, 문이 두 개였습니다
- 대화 세션은 키체인을 읽습니다. 항상 최신입니다.
- 무인 호출(
-p한 방)은 디스크의 자격증명 파일을 읽습니다. 그 파일은 키체인의 사본이고, 비거나 낡습니다.
그래서 이 사고는 사람 눈에 절대 안 보입니다. 제 터미널은 하루 종일 멀쩡했습니다. 죽는 건 새벽과 저녁에 혼자 도는 잡들뿐이었습니다. "무인 시각에만 실패한다"의 진짜 정체는 무인 환경도, 시간대도 아니고 읽는 저장소가 다르다는 것이었습니다.
피해를 세어보니 원장에 7일치(09-02·07·08·11·15·16·18)가 있었고, 주간 롱폼 영상 파이프라인도 한 회차를 통째로 날렸습니다. 그동안 종료코드는 정직하게 1이었고, 저는 정직한 신호를 잘못된 문장으로 해석했습니다.
고친 방법은 싱겁습니다
120초마다 키체인을 읽어 파일과 비교하고, 다르면 파일을 되맞추는 잡 하나입니다. 대신 세 가지를 지킵니다.
- 키체인이 비었거나 만료면 아무것도 쓰지 않습니다. 빈 값으로 덮으면 사고를 제가 내는 겁니다.
- 파일 쪽 만료 시각이 더 미래면 건드리지 않습니다. 되맞춤이 되쓰기가 되면 안 됩니다.
- 토큰 값은 로그에 안 남깁니다. SHA-1 앞 8자리 지문만 남겨 회전 여부만 봅니다.
그리고 생성 잡에는 분기를 하나 넣었습니다. 인증 문구를 보면 포기하기 전에 되맞춤을 한 번 부르고 재시도합니다. 반대로 "사용 한도" 문구는 되맞춤을 부르지 않고 회차를 즉시 멈춥니다 — 벽에 후보를 더 던지는 건 낭비입니다. 처음엔 저도 두 문구를 한 칸에 넣었다가 고쳤습니다. 라벨이 틀리면 조치가 틀립니다. 한도는 기다리면 낫고, 사본 문제는 기다려도 안 낫습니다.
자가진단 3개
- 당신이 쓰는 CLI는 자격증명을 몇 군데에 둡니까?
대화형과비대화형이 같은 곳을 읽는다고 확인해 본 적 있습니까? - 무인 실패를 재현할 때 사람 세션과 같은 환경에서 재현하고 있지는 않습니까? 사람 환경은 대체로 멀쩡합니다.
- 에러 문구가 지목하는 원인(만료·권한·네트워크)을 그 순간의 실제 상태와 대조합니까, 아니면 문구를 믿습니까?
솔직한 부분
이 글은 자랑이 아니라 3주짜리 오진 기록입니다. 제가 놓친 건 어려운 게 아니었습니다. 저장소가 하나라고 가정한 것뿐이었고, 그 가정을 한 번도 검사하지 않았습니다. 심지어 2주 전에 키체인을 한 번 뒤졌다가 "항목 없음"으로 결론 내린 기록도 남아 있습니다 — 그때 조회가 왜 빗나갔는지는 아직 모릅니다. 오늘은 같은 명령이 값을 줬습니다.
무인 잡이 인증 오류로 죽고 있다면, 재로그인하기 전에 딱 하나만 해보세요. 실패하는 그 순간에, 도구가 읽는 저장소와 당신이 보고 있는 저장소가 같은지 확인하는 겁니다. 다르면 거기서 끝납니다.