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

Metadata

Release files for loopstray 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for loopstray 0.1.0
File Size Uploaded Provenance Actions
loopstray-0.1.0.tar.gz 14.6 kB Without provenance Details for loopstray-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for loopstray 0.1.0
File Interpreter ABI Platform Actions
loopstray-0.1.0-py3-none-any.whl Python 3 none any Details for loopstray-0.1.0-py3-none-any.whl

Total release size: 23.6 kB

Release files / loopstray-0.1.0.tar.gz

Download URL loopstray-0.1.0.tar.gz
Size 14.6 kB
Tags Source
SHA-256 checksum
How to use checksums
b369d21f8295db08f53d6684645843868de03f48befb7ae6e07a9ad2765613d5
BLAKE2b-256 checksum
How to use checksums
fd68c12f08efd9232e215d5a2af2d6fbef7a9c5dabcc89384e5a46690c20d753
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Release files / loopstray-0.1.0-py3-none-any.whl

Download URL loopstray-0.1.0-py3-none-any.whl
Size 9.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1ee25b467e7594a57bb924364dce8aa70b1f7f8234b17eedd58e528c98543ea2
BLAKE2b-256 checksum
How to use checksums
fb1a7ebf940e87dbc7cd6d4ecf5d6dd8b3d80bdbc87699245775ce3af2df41a0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Security for loopstray 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

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