affaan-m/ECC · error · ValueError

pack has no measured cadence (cadence.json is missing)

Error message

pack has no measured cadence (cadence.json is missing)

What it means

apply_local refuses to run when the style pack's cadence.json file does not exist at sp.cadence_path. The library plans shot durations from measured cadence data, so without this file there is nothing deterministic to plan from. It fails fast with a ValueError rather than guessing or fabricating timing data.

Solutions

  1. Run the pack's cadence measurement/build step first so cadence.json is generated at sp.cadence_path.
  2. Verify sp.cadence_path points at the right location (print it and check the file exists).
  3. Re-ingest or rebuild the style pack if the file was lost or the directory was moved.
  4. Use a different, complete style pack that already contains cadence.json.

Example fix

// before
report = apply_local(sp, media=clips)  # ValueError: pack has no measured cadence

// after
if not sp.cadence_path.exists():
    sp = build_style_pack(...)  # generates cadence.json from measured shots
report = apply_local(sp, media=clips)
Defensive patterns

Strategy: validation

Validate before calling

if not sp.cadence_path.exists():
    raise FileNotFoundError(f"build cadence.json first at {sp.cadence_path}")

Type guard

def has_cadence(sp) -> bool:
    return sp.cadence_path.is_file()

Try / catch

try:
    report = apply_local(sp, media=clips)
except ValueError as e:
    if "cadence.json is missing" in str(e):
        sp = build_style_pack(...)  # regenerate
        report = apply_local(sp, media=clips)
    else:
        raise

Prevention

When it happens

Trigger: Calling apply_local(sp, media=[...]) when the StylePack's cadence_path does not point to an existing cadence.json — e.g. the pack was never through the measure/forge step, or the pack directory was partially copied.

Common situations: Using a freshly scaffolded style pack before running the cadence measurement step; renaming or moving the pack directory so cadence_path no longer resolves; checking out a repo with the pack's media but not its generated JSON artifacts (gitignored).

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/apply.py:158

    no_repeat: bool = False,
) -> dict[str, Any]:
    """Plan a cut from the pack's cadence over local media clips.

    With ``no_repeat=True``, normalized source paths are used at most once;
    insufficient sources or source durations fail instead of repeating clips.
    Strict plans fill the nearest whole-frame target and never exceed source
    capacity. Media duration metadata must describe the available source.

    Returns an application report validated against
    ``schema.APPLICATION_REPORT_SCHEMA``. The report structurally cannot
    claim a provider run: ``provider`` is enum-locked to ``"none"`` and
    ``dry_run`` to ``true``.
    """
    if not media:
        raise ValueError("apply_local needs at least one media clip")

    if not sp.cadence_path.exists():
        raise ValueError("pack has no measured cadence (cadence.json is missing)")
    cadence = sp.read_json(sp.cadence_path)
    if not isinstance(cadence, dict) or not (cadence.get("shots") or "mean_shot" in cadence):
        raise ValueError("cadence.json has no measured shots to plan from")
    seq_fps = _positive(fps if fps is not None else cadence.get("fps", _DEFAULT_FPS), "fps")
    validated_media = []
    for clip in media:
        if not isinstance(clip, dict) or not isinstance(clip.get("path"), (str, Path)):
            raise ValueError("media clips require a local source path")
        if not str(clip["path"]).strip():
            raise ValueError("media clips require a local source path")
        validated_media.append({**clip, "duration": _positive(clip.get("duration"), "media duration")})
    target = _positive(duration if duration is not None else sum(
        c["duration"] for c in validated_media
    ), "target duration")
    planned = plan_shots(cadence, target)
    assignments = _strict_assign(planned, validated_media, target, seq_fps) if no_repeat else [
        (validated_media[i % len(validated_media)], max(1, timeline.seconds_to_frames(d, seq_fps)))
        for i, d in enumerate(planned)

View on GitHub (pinned to 8321021c54)