affaan-m/ECC · error · RuntimeError

claude -p failed: {result.stderr}

Error message

claude -p failed: {result.stderr}

What it means

generate_spec() calls `claude -p` to produce a compliance spec as YAML. If the subprocess returns non-zero on any attempt (including retries), the loop aborts immediately with the raw stderr. This short-circuits the retry logic, which only retries YAML parse errors — not subprocess failures.

Source

Thrown at skills/skill-comply/scripts/spec_generator.py:51

    for attempt in range(max_retries + 1):
        prompt = base_prompt
        if attempt > 0 and last_error is not None:
            prompt += (
                f"\n\nPREVIOUS ATTEMPT FAILED with YAML parse error:\n"
                f"{last_error}\n\n"
                f"Please fix the YAML. Remember to quote all string values "
                f"that contain colons, e.g.: description: \"Use type: description format\""
            )

        result = subprocess.run(
            ["claude", "-p", prompt, "--model", model, "--output-format", "text"],
            capture_output=True,
            text=True,
            timeout=120,
        )

        if result.returncode != 0:
            raise RuntimeError(f"claude -p failed: {result.stderr}")

        raw_yaml = extract_yaml(result.stdout)

        tmp_path = None
        with tempfile.NamedTemporaryFile(
            mode="w", suffix=".yaml", delete=False,
        ) as f:
            f.write(raw_yaml)
            tmp_path = Path(f.name)

        try:
            return parse_spec(tmp_path)
        except (yaml.YAMLError, KeyError, TypeError) as e:
            last_error = e
            if attempt == max_retries:
                raise
        finally:
            if tmp_path is not None:

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Inspect stderr in the message — it identifies the failure mode.
  2. Verify `claude -p ping --model haiku` works in the same shell.
  3. Export ANTHROPIC_API_KEY or run `claude login`.
  4. Note that retries only cover YAML parse errors, so a transient API failure will not auto-retry — wrap the call and retry at the orchestrator level if needed.

Example fix

# before
spec = generate_spec(skill_path, model='haiku')

# after
import time
for attempt in range(3):
    try:
        spec = generate_spec(skill_path, model='haiku')
        break
    except RuntimeError as e:
        if attempt == 2:
            raise
        time.sleep(2 ** attempt)
Defensive patterns

Strategy: retry

Validate before calling

import os, shutil

def can_generate_spec() -> bool:
    return (
        shutil.which('claude') is not None
        and bool(os.environ.get('ANTHROPIC_API_KEY'))
    )

Try / catch

from scripts.spec_generator import generate_spec
import time
for attempt in range(3):
    try:
        spec = generate_spec(skill_path, model='haiku')
        break
    except RuntimeError as e:
        if attempt == 2:
            raise
        time.sleep(2 ** attempt)

Prevention

When it happens

Trigger: claude CLI missing; ANTHROPIC_API_KEY missing or revoked; network unreachable; subprocess exceeded the 120s timeout (raised as subprocess.TimeoutExpired, a different exception); the model returned an error response that caused non-zero exit.

Common situations: CI without auth; an expired token mid-run; a too-large skill_path that overflowed the context window and made the API return an error.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/4952872472037442. Report an issue: GitHub.