Skip to main content

loopstray

Detects LangChain AgentExecutor(...) (and AgentExecutor.from_agent_and_tools(...)) construction calls with no max_execution_time and/or no max_iterations set -- a poorly-behaved agent stuck in an unconverged reasoning loop can run far longer, and cost far more, than it needs to before anything stops it. Zero runtime dependencies, pure ast-based static analysis -- it never imports or executes the scanned code.

Why

A LangChain AgentExecutor runs a ReAct-style loop: reason, call a tool, observe the result, reason again, until the agent decides it's done. A demo with a handful of test prompts converges quickly every time -- so it ships without either cap ever being set. Then a real user sends a request the agent genuinely can't resolve, and instead of failing, it keeps calling tools and re-reasoning, never converging, burning a fresh LLM API call on every step.

This tool's own verified check of LangChain's source (langchain_classic/agents/agent.py) found the two knobs behave asymmetrically when left unset:

  • max_iterations: int | None = 15 -- there is a hidden default iteration cap. Leaving it unset is not literally infinite looping.
  • max_execution_time: float | None = None -- there is no default wall-clock cap at all. Fifteen iterations, each stuck on a slow tool call or a long reasoning step, can still run for minutes with nothing to stop it early -- often ending only when an infrastructure-level timeout finally kills the request, later and more expensively than an explicit cap would have allowed.

loopstray flags the two omissions at different severities to match this verified, asymmetric reality, rather than treating "neither is set" as one flat check. This is a distinct failure mode from this workspace's llmbrittle (a single LLM call failing with no cross-provider fallback -- the call errors out) and from Node's stallwary (a streaming connection hanging at the network/transport layer). loopstray is about a reasoning loop that never terminates on its own, at the agent-framework level. See DETAILS.md for the full, verified comparison and prior-art check.

Install

pip install loopstray

Usage

loopstray src/                 # scan a directory recursively
loopstray mymodule.py          # scan a single file
loopstray src/ --json          # machine-readable output for CI
loopstray src/ --strict        # also fail on LS002 (warning) findings

Exit codes: 0 clean, 1 an LS001 blocker is present (or any finding under --strict), 2 usage/syntax error.

What it checks (v0.1)

Rule Severity Meaning
LS001 blocker An AgentExecutor construction has no max_execution_time. No wall-clock ceiling exists at all -- LangChain's own default is unbounded time, regardless of any iteration cap.
LS002 warning An AgentExecutor construction has no max_iterations. LangChain's own hidden default of 15 still applies (not literally unbounded), but that default is invisible in this code and not pinned against future library changes.

Suppress a line with a trailing # loopstray: ignore (or # noqa: LS001).

Library usage

from loopstray import scan_file

findings = scan_file("mymodule.py")
for f in findings:
    print(f)

See docs/USAGE.md for more, and DETAILS.md for design rationale, detection scope, and honest limitations.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

loopstray-0.1.0.tar.gz (14.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

loopstray-0.1.0-py3-none-any.whl (9.1 kB view details)

Uploaded Python 3

File details

Details for the file loopstray-0.1.0.tar.gz.

File metadata

  • Download URL: loopstray-0.1.0.tar.gz
  • Upload date:
  • Size: 14.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.12

File hashes

Hashes for loopstray-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b369d21f8295db08f53d6684645843868de03f48befb7ae6e07a9ad2765613d5
MD5 887375fc2a19fa33296aff4f843496f6
BLAKE2b-256 fd68c12f08efd9232e215d5a2af2d6fbef7a9c5dabcc89384e5a46690c20d753

See more details on using hashes here.

File details

Details for the file loopstray-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: loopstray-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 9.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.12

File hashes

Hashes for loopstray-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1ee25b467e7594a57bb924364dce8aa70b1f7f8234b17eedd58e528c98543ea2
MD5 ee9c31f56009a7b6a26fe81f6e5a697b
BLAKE2b-256 fb1a7ebf940e87dbc7cd6d4ecf5d6dd8b3d80bdbc87699245775ce3af2df41a0

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page