Skip to content

fix(warmup): pre-import the agents SDK to avoid a thread-race crash - #1173

Open
samirhvbr wants to merge 1 commit into
usestrix:mainfrom
samirhvbr:fix/warmup-thread-safe-agents-import
Open

fix(warmup): pre-import the agents SDK to avoid a thread-race crash#1173
samirhvbr wants to merge 1 commit into
usestrix:mainfrom
samirhvbr:fix/warmup-thread-safe-agents-import

Conversation

@samirhvbr

Copy link
Copy Markdown

Problem

Real scans intermittently crash at startup with:

ImportError: cannot import name 'AgentOutputSchemaBase' from partially
initialized module 'agents.agent_output' (most likely due to a circular import)

--version/--help never hit it — the tell that it only happens on the scan path.

Root cause

start_import_warmup() runs a daemon thread that imports strix.core.runner → the
openai-agents SDK. On the scan path the main thread independently imports the
same package via strix.reportstrix.report.dedupe
from agents.models.interface import ModelTracing.

openai-agents has an internal import cycle (agents.agent_outputagents.agent,
reached through agents/__init__.pysandboxrun_configlifecycle
agent). A single thread resolves the cycle, but when two threads import the
agents package concurrently, CPython's per-module import locks + deadlock-avoidance
can let one thread proceed with a partially initialized module — producing the
ImportError above. It is scheduler-dependent, which matches the intermittent reports.

Fix

Import the agents package once, synchronously, on the caller's thread before the
daemon warm-up thread starts, so it is fully in sys.modules before any concurrent
import. start_import_warmup() is also moved to just after parse_arguments() so
--version/--help/argparse errors exit before the (now partly synchronous) warm-up
runs; the warm-up still overlaps the Docker checks and image pull that follow.

Test

Adds tests/test_warmup.py: in a fresh interpreter, with an empty warm set so the
daemon thread imports nothing, start_import_warmup() must leave agents in
sys.modules — proving the synchronous pre-import ran.

ruff format --check, ruff check, and the new test pass locally.

Original traceback
File ".../strix/interface/main.py", line 454, in main
    from strix.report.state import get_global_report_state
File ".../strix/report/__init__.py", line 3, in <module>
    from strix.report.dedupe import check_duplicate
File ".../strix/report/dedupe.py", line 11, in <module>
    from agents.models.interface import ModelTracing
File ".../agents/models/interface.py", line 10, in <module>
    from ..agent_output import AgentOutputSchemaBase
File ".../agents/agent_output.py", line 11, in <module>
    from .util import _error_tracing, _json
File ".../agents/__init__.py", line 8, in <module>
    from . import _config, sandbox
File ".../agents/sandbox/__init__.py", line 3, in <module>
    from ..run_config import SandboxArchiveLimits, SandboxConcurrencyLimits, SandboxRunConfig
File ".../agents/run_config.py", line 19, in <module>
    from .lifecycle import RunHooks
File ".../agents/lifecycle.py", line 5, in <module>
    from .agent import Agent, AgentBase
File ".../agents/agent.py", line 15, in <module>
    from .agent_output import AgentOutputSchemaBase
ImportError: cannot import name 'AgentOutputSchemaBase' from partially initialized
module 'agents.agent_output' (most likely due to a circular import)

🤖 Generated with Claude Code

The background import warm-up thread imports strix.core.runner (and thus the
openai-agents SDK) while, on the scan path, the main thread independently
imports the same package via strix.report -> strix.report.dedupe ->
`from agents.models.interface import ModelTracing`.

openai-agents has an internal import cycle (agents.agent_output <->
agents.agent). A single thread resolves it, but when two threads import the
`agents` package concurrently, CPython's per-module import locks +
deadlock-avoidance can hand one thread a partially initialized module, crashing
the run with:

    ImportError: cannot import name 'AgentOutputSchemaBase' from partially
    initialized module 'agents.agent_output' (most likely due to a circular
    import)

--version/--help never hit it because they don't import agents; it only shows
up on a real scan, which is why it looked intermittent.

Import the `agents` package once, synchronously, on the caller's thread before
the daemon warm-up thread starts, so it is fully initialized before any
concurrent import. Also start the warm-up after parse_arguments() so
--version/--help/argparse errors exit before the now-partly-synchronous
warm-up runs; it still overlaps the Docker checks and image pull that follow.

Adds tests/test_warmup.py asserting start_import_warmup() leaves `agents` in
sys.modules synchronously.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 26, 2026 11:52
@greptile-apps

greptile-apps Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR synchronously imports the Agents SDK before starting background import warmup, closing the reported concurrent-import race. It also moves warmup after argument parsing so early-exit CLI paths remain lightweight and adds a fresh-interpreter regression test.

  • Pre-imports agents on the caller thread while preserving non-fatal warmup behavior.
  • Starts background warmup after CLI arguments have been parsed.
  • Verifies that start_import_warmup() synchronously places agents in sys.modules.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete changed-code failure identified.

The synchronous SDK import completes before the background thread starts, argument parsing does not independently reach the SDK, and the added test isolates the intended pre-import behavior.

Important Files Changed

Filename Overview
strix/interface/main.py Moves import warmup after argument parsing without exposing an earlier agents import or disrupting scan startup ordering.
strix/llm/warmup.py Adds a caller-thread SDK import before daemon startup, directly serializing the two import paths implicated in the crash.
tests/test_warmup.py Uses a fresh interpreter and an empty background module set to isolate and verify the synchronous pre-import behavior.

Reviews (1): Last reviewed commit: "fix(warmup): pre-import the agents SDK t..." | Re-trigger Greptile

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes an intermittent startup crash caused by concurrent imports of the openai-agents (agents) SDK by ensuring the SDK is imported once, synchronously, before the background warm-up thread begins. This reduces the likelihood of CPython returning a partially-initialized module during circular-import resolution on multi-threaded import paths.

Changes:

  • Pre-import agents synchronously inside start_import_warmup() before spawning the daemon warm-up thread.
  • Move the warm-up start to after parse_arguments() so --help/--version and argparse errors exit before doing any heavy warm-up work.
  • Add a subprocess-based regression test to assert that start_import_warmup(modules=()) still leaves agents in sys.modules (proving the synchronous pre-import ran).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
strix/llm/warmup.py Adds a synchronous pre-import of the thread-unsafe agents SDK ahead of the daemon import warm-up thread.
strix/interface/main.py Reorders startup so argument parsing happens before warm-up starts, avoiding warm-up on early-exit CLI flows.
tests/test_warmup.py Adds a fresh-interpreter test that verifies agents is synchronously imported by start_import_warmup().

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread strix/llm/warmup.py
Comment on lines +69 to 72
_preimport_thread_unsafe_sdk()
_thread = threading.Thread(
target=_warm, args=(modules,), name="strix-import-warmup", daemon=True
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants