{"record":{"id":"cbcb71e1bf91d028","repo":"affaan-m/ECC","slug":"effect-event-start-and-duration-must-be-finite-positive","errorCode":null,"errorMessage":"effect event start and duration must be finite positive timeline values","messagePattern":"effect event start and duration must be finite positive timeline values","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":200,"sourceCode":"    if not isinstance(recipe.get(\"seed\"), int) or isinstance(recipe.get(\"seed\"), bool):\n        raise ContractError(\"effect recipe must have an integer seed\")\n    if recipe.get(\"rng_algorithm\") != \"python.random.Random/v1\":\n        raise ContractError(\"effect recipe must declare its seeded RNG algorithm\")\n    events = recipe.get(\"events\")\n    if not isinstance(events, list) or len(events) < 3:\n        raise ContractError(\"effect recipe needs at least three scheduled events\")\n    timeline = recipe.get(\"timeline_duration\")\n    if not _is_finite_real(timeline):\n        raise ContractError(\"effect recipe must declare a finite positive timeline duration\")\n    timeline = cast(float, timeline)\n    if float(timeline) <= 0:\n        raise ContractError(\"effect recipe must declare a finite positive timeline duration\")\n    for event in events:\n        start = event.get(\"time\")\n        duration = event.get(\"duration\")\n        if (not _is_finite_real(start) or not _is_finite_real(duration)\n                or float(start) < 0 or float(duration) <= 0):\n            raise ContractError(\"effect event start and duration must be finite positive timeline values\")\n        start = cast(float, start)\n        duration = cast(float, duration)\n        if float(start) + float(duration) > float(timeline) + 1e-9:\n            raise ContractError(\"effect event end exceeds the declared timeline\")\n        evidence = event.get(\"evidence\")\n        if not isinstance(evidence, dict):\n            raise ContractError(f\"effect {event.get('effect')} lacks reference evidence\")\n        _validate_media_time(\n            evidence.get(\"time\"), evidence.get(\"source_duration\"), label=\"effect evidence time\"\n        )\n        if reference_durations is not None:\n            digest = evidence.get(\"reference_sha256\")\n            expected_duration = reference_durations.get(digest) if isinstance(digest, str) else None\n            if expected_duration is None or evidence.get(\"source_duration\") != expected_duration:\n                raise ContractError(\"effect evidence source duration is not bound to its receipt reference\")\n    times = [float(event[\"time\"]) for event in events]\n    if times != sorted(times) or len(times) != len(set(times)):\n        raise ContractError(\"effect event times must be unique and increasing\")","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L182-L218","documentation":"Each event in the schedule must have a 'time' (>= 0) and a 'duration' (> 0) that are both finite real numbers. Missing keys, strings, bools, NaN/inf, negative starts, or zero/negative durations all raise this error.","triggerScenarios":"validate_effect_recipe called where any event dict lacks 'time' or 'duration', stores them as strings (e.g. from JSON/config), uses NaN/inf, has time < 0, or duration <= 0.","commonSituations":"Durations parsed from YAML/JSON kept as strings like '2.5'; a default duration of 0; negative start times from clip-relative offsets; None filling missing optional fields.","solutions":["Ensure every event has numeric time >= 0 and duration > 0 (floats or ints).","Convert string/None values to floats before validation and reject non-numeric ones.","Clamp or reject negative starts and zero durations at recipe-generation time."],"exampleFix":"# before\nevent = {\"time\": \"0.5\", \"duration\": 0}\n# after\nevent = {\"time\": 0.5, \"duration\": 2.0}","handlingStrategy":"validation","validationCode":"for ev in recipe[\"events\"]:\n    t, d = ev.get(\"time\"), ev.get(\"duration\")\n    ok = (isinstance(t, (int, float)) and not isinstance(t, bool) and math.isfinite(t) and t >= 0\n        and isinstance(d, (int, float)) and not isinstance(d, bool) and math.isfinite(d) and d > 0)\n    if not ok:\n        raise ValueError(f\"event has invalid time/duration: {t!r}, {d!r}\")","typeGuard":"def event_time_valid(ev: dict) -> bool:\n    t, d = ev.get(\"time\"), ev.get(\"duration\")\n    return (isinstance(t, (int, float)) and not isinstance(t, bool)\n        and math.isfinite(t) and t >= 0\n        and isinstance(d, (int, float)) and not isinstance(d, bool)\n        and math.isfinite(d) and d > 0)","tryCatchPattern":"try:\n    validate_effect_recipe(recipe)\nexcept ContractError as exc:\n    if \"finite positive timeline values\" in str(exc):\n        recipe = {**recipe, \"events\": [normalize_event_times(ev) for ev in recipe[\"events\"]]}\n        validate_effect_recipe(recipe)\n    else:\n        raise","preventionTips":["Coerce event time/duration to float at config load, rejecting non-numeric strings.","Default durations to a positive constant, never 0.","Reject negative starts at generation time."],"tags":["validation","events","value-range"],"backgroundTag":"value-out-of-range","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"}