Skip to content

OSError Errno 36 crash - Namespace repr leaking into subprocess argv) #597

@JohnRSim

Description

@JohnRSim

desloppify detect phpstan_error (and likely any detector via detect) crashes with OSError: [Errno 36] File name too long once the project accumulates a modest number of zone overrides. The stringified argparse.Namespace (including the full runtime.config dict with all zone_overrides) is being passed where subprocess.Popen expects a path, so ENAMETOOLONG fires as soon as the Namespace repr() exceeds the kernel's filename limit.

Impact for me: after hitting this, scan could no longer refresh phpstan_error findings, and the corresponding state entries became stuck even though phpstan analyse itself exits 0. I had to desloppify suppress phpstan_error::src/ with a truthful attest to unblock.

Environment

  • desloppify 0.9.15 (pipx)
  • Python 3.12.3
  • Linux 6.6.87.2-microsoft-standard-WSL2 (Ubuntu)
  • Language: PHP (mid-tier plugin, tool = phpstan, depth 5/8)

Reproduction

  1. In any project scanned by desloppify, apply ~20+ zone overrides (e.g. one per test file):
    for f in tests/Unit/**/*.php; do desloppify zone set "$f" test; done
  2. Run any detector subcommand:
    desloppify detect phpstan_error

With ~21 overrides on my project, this fails 100% of the time.

Expected

The detector re-runs phpstan and refreshes phpstan_error::… findings in state.

Actual

OSError: [Errno 36] File name too long: 'Namespace(lang=None, exclude=None, command=\'detect\',
detector=\'phpstan_error\', top=20, path=\'/home/johns/repos/querri-phpembed\', json=False,
fix=False, category=\'all\', threshold=None, file=None, lang_opt=None,
runtime=CommandRuntime(config={\'target_strict_score\': 85, …, \'zone_overrides\':
{\'tests/Unit/ConfigTest.php\': \'test\', \'tests/Unit/Exceptions/ApiExceptionTest.php\': \'test\',
…21 entries…}, …}))'

Full traceback:

File ".../desloppify/cli.py", line 284, in main
    handler(args)
File ".../desloppify/app/commands/detect.py", line 87, in cmd_detect
    lang.detect_commands[detector](args)
File ".../desloppify/languages/_framework/generic_parts/tool_factories.py", line 121, in detect
    result = run_tool_result(cmd, path, parser, run_subprocess=run_subprocess)
File ".../desloppify/languages/_framework/generic_parts/tool_runner.py", line 74, in run_tool_result
    result = runner(
File "/usr/lib/python3.12/subprocess.py", line 548, in run
    with Popen(*popenargs, **kwargs) as process:
File "/usr/lib/python3.12/subprocess.py", line 1955, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
OSError: [Errno 36] File name too long: 'Namespace(...)'

Suspected cause

In generic_parts/tool_runner.py:74, run_tool_result's runner(...) call appears to be receiving the full args Namespacewhere subprocess.run expects either a list/string command or an executable=<path> kwarg. The Namespace's __str__() isbeing coerced into a path and tripping ENAMETOOLONG as soon as its length exceeds ~255 bytes — which happens easily oncezone_overrides has ~20 entries.

The underlying detector command (phpstan in this case) is never actually invoked.

Suggested fix direction (guess, not verified)

tool_runner.py should pass only the resolved command list + working directory to subprocess.run, never the argparse Namespace. A defensive check/type-hint on runner's inputs would surface this at call sites rather than deep in _execute_child.

Workaround

desloppify config unset exclude and clearing zone overrides with desloppify zone clear <file> restores detect to a working state, at the cost of losing the classification signal the overrides provided. desloppify suppress<detector>::<prefix> with an attest is a runtime workaround for the stale-findings symptom.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions