refactor: reconcile with #93269 — one shared shutdown predicate, keep both guard sites

PR #93269 (kshitijk4poor) landed the outer-handler break for the same
symptom while this branch was in flight. Keep his guard (it covers
shutdown errors from local post-processing and does the resume-hint +
best-effort persist) and keep this branch's inner-retry-handler return
(it fires BEFORE the ⚠️ retry trace, credential rotation, and fallback
attempts that the outer handler never sees). Point his
_is_interpreter_shutdown_error at tools/interpreter_shutdown.py so the
class has exactly one text-matching site, preserving his RuntimeError
type gate and all 7 of his tests.
This commit is contained in:
Teknium
2026-08-23 16:01:08 -07:00
parent 9ea7fe9938
commit 26cf456598
+10 -35
View File
@@ -279,15 +279,18 @@ def _is_interpreter_shutdown_error(exc: Exception) -> bool:
During teardown, ``concurrent.futures`` refuses new work with
``RuntimeError: cannot schedule new futures after interpreter shutdown``
(or the shorter ``... after shutdown`` variant from a plain
ThreadPoolExecutor). Both are documented in #58720. The common
prefix catches both; the module-global ``is_finalizing`` flag can
lag the error by a hair, so matching the error text is the safe
fallback for that race.
ThreadPoolExecutor). Both are documented in #58720.
Delegates to the shared predicate in ``tools.interpreter_shutdown``
(same home as cron delivery and concurrent tool submission) so the
shutdown-race bug class has one text-matching site. Keeps the
RuntimeError type gate from the original (#93269): unlike the raw
predicate, a ValueError carrying similar text must not match here.
"""
if isinstance(exc, RuntimeError):
msg = str(exc).lower()
if "cannot schedule new futures" in msg:
return True
from tools.interpreter_shutdown import interpreter_shutting_down
return interpreter_shutting_down(exc)
return False
@@ -8410,34 +8413,6 @@ def run_conversation(
_is_local_processing_error = _hit_local and not _hit_api
# Interpreter finalization: the process is exiting (TUI quit,
# SIGTERM, one-shot CLI done) while a background turn — most
# commonly the post-turn review fork's daemon thread — is still
# mid-request. Every further API attempt raises "cannot schedule
# new futures after interpreter shutdown", so retrying is futile
# and the un-gated ❌ prints spam the user's shell AFTER the TUI
# already exited (call #4, #5, #6...). Same shutdown-race class
# as cron delivery (#55924/#58720) and concurrent tool submission;
# shared predicate in tools/interpreter_shutdown.py. Log one
# warning, no traceback, no synthetic history append (nothing can
# persist it anymore), and leave the loop immediately.
from tools.interpreter_shutdown import interpreter_shutting_down
if interpreter_shutting_down(e):
logger.warning(
"Interpreter is shutting down — abandoning turn after "
"API call #%d (%s)",
api_call_count,
e,
)
_turn_exit_reason = "interpreter_shutdown"
failed = True
final_response = (
"Turn abandoned: the process was shutting down before "
"the model call could complete."
)
break
if _is_local_processing_error:
error_msg = (
f"Error during local message processing after "