무인으로 도는 잡 두 개가 같은 문장을 남기고 죽었습니다.
Failed to authenticate: OAuth session expired and could not be refreshed읽으면 할 일이 명확해 보입니다. 세션이 만료됐으니 다시 로그인하면 됩니다. 저는 3주 동안 그렇게 했습니다.
당신의 에러 메시지는, 원인을 말하고 있습니까 아니면 증상을 말하고 있습니까?
재로그인이 그날 저녁을 못 넘겼습니다
09월 16일 오전 9시 30분에 사람이 직접 재로그인했습니다. 곧바로 확인도 했습니다. 정상이었습니다.
같은 날 오후 4시, 주간 다큐 잡이 같은 문장으로 죽었습니다. 저녁 6시 5분, 블로그 영상 대본을 만드는 잡도 같은 문장으로 죽었습니다.
"재로그인하면 며칠 간다"가 아니었습니다. 이 시점에서 만료 가설은 이미 흔들렸지만, 저는 여전히 가설을 반증할 데이터가 없었습니다. 실패한 순간에 파일이 어땠는지 아무도 기록하지 않았기 때문입니다.
실패 순간을 찍기로 했습니다
고치는 대신 계측을 붙였습니다. claude -p 호출이 실패할 때마다, 그 직전과 직후의 자격증명 상태를 한 줄로 남기게 했습니다. 토큰 값은 남기지 않고 SHA-1 지문 8자리와 남은 수명만 적었습니다.
18:05:06 claude_fail rc=1 OAuth session expired and could not be refreshed
18:05:06 cred before[mtime_age=90s exp_in=28708s at=d3b42fa6 procs=6]exp_in=28708. 28,708초 = 7시간 58분이 남아 있었습니다.
만료된 토큰으로 죽은 게 아니었습니다. 완벽하게 유효한 토큰을 들고 "세션이 만료되었다"는 답을 받은 겁니다. 3주 동안 제가 고치려던 것은 애초에 고장 나 있지 않았습니다.
그럼 누가 망가뜨렸나
자격증명 파일을 2분마다 찍는 별도 기록을 같이 켜두고 있었습니다. 같은 날 저녁 구간이 이렇게 나왔습니다.
| 시각 | 남은 수명 | 토큰 지문 |
|---|---|---|
| 17:41 | -132,452초 | 4253adcf |
| 17:43 | — | da39a3ee |
| 18:01 | — | da39a3ee |
| 18:03 | — | da39a3ee |
| 18:05 | 28,692초 | d3b42fa6 |
두 줄이 눈에 띕니다.
17시 41분: 새로 쓰인 토큰의 수명이 마이너스입니다. 만료가 1.5일 지난 값이 그 순간 파일에 기록됐습니다.
17시 43분·18시 01분·18시 03분: 지문이 da39a3ee입니다. 이건 빈 문자열의 SHA-1 앞 8자리입니다. 파일은 있는데 토큰 칸이 비어 있었습니다.
당신이라면 여기서 뭘 의심하겠습니까
파일을 망가뜨린 게 프로그램인지 사람인지, 아니면 저장소가 깨진 건지. 저는 프로세스 목록을 봤습니다.
4918 Mon Sep 14 09:04 02-23:48 (대화 세션)
26217 Tue Sep 15 09:12 01-23:40 (대화 세션)
68713 Tue Sep 15 10:08 01-22:45 (대화 세션, 무한 대기 중)1.5일 지난 토큰을 메모리에 들고 있을 수 있는 건 1.5일 넘게 살아 있는 프로세스뿐입니다. 터미널 탭에 열어두고 잊어버린 대화 세션 네 개가 그것이었습니다. 하나는 배포 결과를 기다리는 루프에 걸려 이틀째 sleep 중이었습니다.
오래 산 세션이 자기 메모리의 낡은 자격증명을 파일에 되쓰고, 마침 그 순간에 인증하던 무인 잡이 죽은 겁니다.
"무인 시각에만 실패한다"의 정체도 이걸로 풀렸습니다. 무인 환경이 특별해서가 아니라, 그 시각에 사람 세션이 여럿 살아 있었기 때문입니다. 저는 2주 전에 "같은 분에 겹치는 잡이 없으니 경합이 아니다"라고 판단하고 경합 가설을 접었습니다. 겹친 건 잡이 아니라 터미널이었습니다.
덤: 계측 도구가 범인을 가리고 있었습니다
동시 실행 수를 pgrep -fl claude로 세고 있었습니다. 같은 순간에 세어보니 5개였습니다. -af로 세니 7개였습니다.
pgrep은 부르는 쪽의 조상 프로세스를 빼고 돌려줍니다. 하필 그게 제일 오래 산 세션입니다. 범인을 세는 도구가 범인을 목록에서 빼고 있었습니다.
자가진단 3개
- 당신의 실패 로그는 실패한 순간의 상태를 남깁니까, 아니면 실패했다는 사실만 남깁니까?
- 공유 자격증명 파일에 쓰는 주체가 몇 개입니까? 오래 사는 프로세스가 그 목록에 있습니까?
- 프로세스를 세는 코드가 자기 자신과 부모를 세고 있습니까?
솔직한 부분
원인은 아직 확정이 아닙니다. 되쓰기가 맞다면 오래된 세션을 비운 뒤 재발이 멎어야 합니다. 그래서 오늘 한 일은 수정이 아니라 두 가지입니다 — 24시간 넘은 세션을 정리한 것, 그리고 다음에 빈 토큰이나 지나간 토큰이 기록되는 순간 그 시각의 프로세스 목록 전체를 원장에 박게 한 것입니다.
3주 동안 제가 한 것은 에러 메시지를 읽고 그 문장이 시키는 대로 한 일이었습니다. 문장은 증상이었고, 증상은 원인이 아니었습니다. 게이트가 0만 잡고 있어서 고친 버그가 8일을 더 산 적도 같은 계보입니다 — 재는 도구를 안 의심하면 고치는 쪽만 계속 바뀝니다.
지금 당신의 무인 잡 하나를 골라, 마지막 실패 줄 옆에 그 순간의 상태가 적혀 있는지 확인해보세요. 없다면, 다음 실패도 똑같이 추측으로 끝납니다.