affaan-m/ECC · error · ValueError

local-only preservation cannot contain candidates or inserts

Error message

local-only preservation cannot contain candidates or inserts

What it means

Local-only mode is strictly a preservation bundle: it must contain zero candidates and zero inserts. If either collection is non-empty while local_only is True, the bundle would silently mix provider-derived edits into a 'preservation' artifact, so it is rejected.

Solutions

  1. Set cfg['candidates'] = [] and cfg['inserts'] = [] before local-only bundling
  2. Build the bundle with local_only=False if you actually want the candidates/inserts applied
  3. Split the config: preservation-only config for local_only, full config for hosted mode

Example fix

// before
build_application_bundle({**cfg, "inserts": [ins]}, None, local_only=True)
// after
build_application_bundle({**cfg, "inserts": [], "candidates": []}, None, local_only=True)
Defensive patterns

Strategy: validation

Validate before calling

if local_only and (cfg.get("candidates") or cfg.get("inserts")):
    raise ValueError("local-only mode requires empty candidates and inserts")

Type guard

def is_preservation_only(cfg):
    return not cfg.get("candidates") and not cfg.get("inserts")

Try / catch

try:
    bundle = build_application_bundle(cfg, None, local_only=True)
except ValueError as e:
    if "cannot contain candidates or inserts" in str(e):
        cfg = {**cfg, "candidates": [], "inserts": []}
        bundle = build_application_bundle(cfg, None, local_only=True)
    else:
        raise

Prevention

When it happens

Trigger: build_application_bundle(cfg, None, local_only=True) where cfg['candidates'] or cfg['inserts'] is a non-empty list (e.g. leftover insert entries from a previous hosted run).

Common situations: Re-running a previous hosted bundle build with local_only flipped on without clearing proposals; a generic template that always emits empty-ish but non-empty default candidate arrays.

Related errors


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

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/integration.py:360

    _object(config, _REQUIRED, _OPTIONAL)
    if len(_canonical(config)) > _MAX_JSON:
        raise ValueError("application config exceeds local size limit")
    if local_only:
        if compiled_input is not None:
            raise ValueError("local-only preservation cannot accept provider input")
    else:
        _object(compiled_input, {"source_video", "compiled_prompt"})
        url = urlsplit(_text(compiled_input["source_video"]))
        if url.scheme != "https" or not url.hostname or url.username or url.password:
            raise ValueError("source reference must be HTTPS without embedded credentials")
        _text(compiled_input["compiled_prompt"])
    cfg = copy.deepcopy(config)
    for key in ("audio", "candidates", "inserts", "historical_receipts"):
        cfg.setdefault(key, [])
        if not isinstance(cfg[key], list):
            raise ValueError("bundle collections must be lists")
    if local_only and (cfg["candidates"] or cfg["inserts"]):
        raise ValueError("local-only preservation cannot contain candidates or inserts")
    tracks = _snapshot(cfg["baseline"])
    _binding(cfg["source"], tracks, cfg["baseline"])
    audio_keys = [_binding(item, tracks, cfg["baseline"], audio=True) for item in cfg["audio"]]
    expected_audio = {(t, i) for t, clips in tracks.items() if t.startswith("audio")
                      for i in range(len(clips))}
    if len(set(audio_keys)) != len(audio_keys) or set(audio_keys) != expected_audio:
        raise ValueError("every original audio clip must be preserved exactly once")
    stack = _preserved_stack(cfg["protected_intervals"], tracks, cfg["baseline"]["timeline_range"])
    input_hash = None if local_only else _digest(compiled_input)
    edit_hash = _digest({key: cfg[key] for key in _REQUIRED})
    if not local_only:
        candidates = _candidates(cfg["candidates"], cfg["source"]["media"]["sha256"],
                                 input_hash, compiled_input["source_video"])
        _inserts(cfg["inserts"], candidates, cfg, input_hash, edit_hash)
    for receipt in cfg["historical_receipts"]:
        _artifact(receipt)
    result = {**cfg, "schema_version": 1, "mode": "preserve_native_timeline",
              "provider_calls": 0, "provider_execution": False, "dry_run": True, "submit": False,

View on GitHub (pinned to 8321021c54)