affaan-m/ECC · error · SystemExit

stage ' ' failed with exit

Error message

stage '{label}' failed with exit {proc.returncode}

What it means

pipeline.py's _run wraps a subprocess stage of the full chain; when the child process exits non-zero it raises SystemExit with a message naming the stage label and exit code. It converts an arbitrary downstream tool failure into a uniform, labeled pipeline abort.

Solutions

  1. Read the child stage's output printed just above the failure to find the root cause
  2. Re-run only the failed stage directly to iterate faster and see full stderr
  3. Fix the underlying stage-specific problem (missing refs, name collision, etc.) before rerunning the pipeline
  4. Check exit code semantics: nonzero codes like 2 from verify indicate check failures rather than crashes

Example fix

// before
python pipeline.py --genre noir --refs ref.mp4   # stage 'mint' failed with exit 1
// after
python mint.py noir --refs ref.mp4   # run stage directly to see full error, then rerun pipeline
Defensive patterns

Strategy: try-catch

Try / catch

try:
    pipeline.main()
except SystemExit as e:
    m = re.search(r"stage '(\w+)' failed with exit (\d+)", str(e))
    if m:
        stage, code = m.groups()
        rerun_stage_directly(stage)  # see full stderr there

Prevention

When it happens

Trigger: Any wrapped stage script (mint.py, mint3d.py, forge.py, resolve_ingest.py, verify) exits non-zero - e.g. due to the other errors in this set: unreadable refs, existing outputs, missing stills, invalid names.

Common situations: Chained runs where an early stage fails and the pipeline halts with only 'stage X failed with exit N'; Python's default exit 1 from an uncaught exception in a stage, or exit 2 from a failed verify.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/33aa1898e8b3ceed. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/pipeline.py:48

import sys
import time
from pathlib import Path


SCRIPTS = Path(__file__).resolve().parent


def _command(cmd: list[str]) -> list[str]:
    """Resolve tools, while retaining caller-relative media and output paths."""
    return [sys.executable, str(SCRIPTS / cmd[0]), *cmd[1:]]


def _run(label: str, cmd: list[str]) -> None:
    print(f"\n{'=' * 70}\n[{label}] {' '.join(cmd[:6])} ...\n{'=' * 70}")
    t = time.time()
    proc = subprocess.run(_command(cmd))
    if proc.returncode != 0:
        raise SystemExit(f"stage '{label}' failed with exit {proc.returncode}")
    print(f"[{label}] done in {time.time() - t:.0f}s")


def main() -> None:
    ap = argparse.ArgumentParser(description="Run the full taste-forge chain.")
    ap.add_argument("--genre", required=True)
    ap.add_argument("--refs", nargs="*", default=None,
                    help="reference videos; omit to reuse an existing pack")
    ap.add_argument("--takes", nargs="+", help="existing local takes; skips every provider stage")
    ap.add_argument("--fps", type=float, default=None, help="output frame rate")
    ap.add_argument("--brief", default="", help="WHAT HAPPENS in the new piece")
    ap.add_argument("--style-steer", default="", help="HOW IT LOOKS, per-run nudge")
    ap.add_argument("--duration", type=float, default=12.0,
                    help="best-effort cadence target in seconds, not an exact duration; actual result is reported")
    ap.add_argument("--base-video", default=None, help="existing footage to supplement")
    ap.add_argument("--base-ratio", type=float, default=0.35)
    ap.add_argument("--out", default=None)
    ap.add_argument("--root", default="stylepacks")

View on GitHub (pinned to 8321021c54)