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
- Set cfg['candidates'] = [] and cfg['inserts'] = [] before local-only bundling
- Build the bundle with local_only=False if you actually want the candidates/inserts applied
- 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
- Keep separate config templates for preservation vs edit bundles
- Clear proposals before switching to local-only mode
- Assert empty proposal collections in local-mode CI checks
- Document the invariant in your bundle-authoring guide
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
- local-only preservation cannot accept provider input
- -32602
- a claim token is required
- a confirmed nonempty coordinate is required
- a generated candidate cannot claim original-source identity
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)