{"record":{"id":"a2560bad7778dc72","repo":"affaan-m/ECC","slug":"native-snapshot-must-be-an-object","errorCode":null,"errorMessage":"native snapshot must be an object","messagePattern":"native snapshot must be an object","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/integration.py","lineNumber":195,"sourceCode":"        return result\n\n    try:\n        value = json.loads(data, object_pairs_hook=unique)\n        _canonical(value)\n        return value\n    except (UnicodeError, RecursionError) as exc:\n        raise ValueError(\"invalid JSON artifact\") from exc\n\n\ndef _snapshot(baseline: dict) -> dict:\n    _object(baseline, {\"project_file\", \"snapshot_file\", \"project_name\", \"timeline_name\",\n                       \"fps\", \"timeline_range\"})\n    _rate(baseline[\"fps\"])\n    bounds = _range(baseline[\"timeline_range\"])\n    _artifact(baseline[\"project_file\"])\n    snapshot = _artifact(baseline[\"snapshot_file\"], parse_json=True)\n    if not isinstance(snapshot, dict):\n        raise ValueError(\"native snapshot must be an object\")\n    settings = snapshot.get(\"settings\")\n    native_fps = settings.get(\"timelineFrameRate\") if isinstance(settings, dict) else None\n    # Resolve's conventional decimal NTSC labels represent these exact rates.\n    ntsc = {\"23.976\": \"24000/1001\", \"29.97\": \"30000/1001\", \"59.94\": \"60000/1001\"}\n    try:\n        if type(native_fps) not in (str, int, float):\n            raise ValueError(\"native fps missing\")\n        label = str(native_fps)\n        if not re.fullmatch(r\"[0-9]{1,9}(?:\\.[0-9]{1,12}|/[1-9][0-9]{0,8})?\", label):\n            raise ValueError(\"native fps must use a bounded decimal or rational label\")\n        native_rate = Fraction(ntsc.get(label, label))\n        if native_rate != Fraction(*_rate(baseline[\"fps\"])):\n            raise ValueError(\"native fps differs\")\n    except (ValueError, ZeroDivisionError) as exc:\n        raise ValueError(\"native snapshot fps is missing, invalid or contradictory\") from exc\n    for cfg, key in [(\"project_name\", \"project\"), (\"timeline_name\", \"timeline\")]:\n        if _text(baseline[cfg]) != snapshot.get(key):\n            raise ValueError(\"native snapshot identity mismatch\")","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/integration.py#L177-L213","documentation":"_snapshot validates the DaVinci Resolve native snapshot referenced by baseline.snapshot_file. After parsing it as JSON, it requires the top-level value to be a JSON object; arrays or scalars cannot carry settings/timeline_readback, so the library throws this error.","triggerScenarios":"build_application_bundle -> _snapshot where snapshot_file's JSON parses successfully but its root is an array (e.g. a list of snapshots), a string, a number, or null.","commonSituations":"Exporting a batch of snapshots as a JSON array instead of one object; a snapshot exporter writing `null` when no timeline was open; wrapping the snapshot in an outer array during a migration.","solutions":["Point baseline.snapshot_file at the single-object snapshot export, not an array of snapshots.","If you have an array, select the intended element and write it as the root object.","Fix the exporter so it emits a single JSON object per snapshot file.","Re-export the snapshot from Resolve and confirm the file starts with `{`."],"exampleFix":"// before (snapshot.json)\n[{\"project\": \"P\", \"timeline\": \"T\", ...}]\n\n// after\n{\"project\": \"P\", \"timeline\": \"T\", \"settings\": {...}, \"timeline_readback\": {...}}","handlingStrategy":"validation","validationCode":"import json\ndef check_snapshot_object(path):\n    snap = json.load(open(path, encoding=\"utf-8\"))\n    if not isinstance(snap, dict):\n        raise TypeError(f\"{path}: snapshot root must be an object\")\n    if not isinstance(snap.get(\"settings\"), dict) or \"timelineFrameRate\" not in snap[\"settings\"]:\n        raise TypeError(f\"{path}: missing settings.timelineFrameRate\")","typeGuard":"def is_snapshot(value) -> bool:\n    return isinstance(value, dict) and isinstance(value.get(\"settings\"), dict)","tryCatchPattern":"try:\n    bundle = build_application_bundle(config, None, local_only=True)\nexcept ValueError as exc:\n    if \"must be an object\" in str(exc):\n        print(\"snapshot_file JSON root is not an object — re-export a single snapshot\")\n    raise","preventionTips":["Export one snapshot per file, as a single JSON object.","Validate snapshot shape right after export.","Keep settings.timelineFrameRate intact when trimming snapshots.","Pin snapshot files to the timeline they came from."],"tags":["json","validation","snapshot"],"backgroundTag":"type-mismatch","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}