{"record":{"id":"4700432138fb7287","repo":"666ghj/MiroFish","slug":"chart-coordinates-must-be-finite","errorCode":null,"errorMessage":"chart coordinates must be finite","messagePattern":"chart coordinates must be finite","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":786,"sourceCode":"    if value < 0:\n        return -1\n    if value > 0:\n        return 1\n    return 0\n\n\ndef _monotone_x_path(points: Sequence[tuple[float, float]]) -> str:\n    \"\"\"Return a D3 curveMonotoneX-equivalent SVG path.\n\n    D3 uses Steffen monotonic interpolation: interior tangents are limited so a\n    smooth cubic cannot overshoot a monotonic run. This small implementation\n    keeps the Star History curve shape without adding a JavaScript dependency.\n    \"\"\"\n\n    normalized: list[tuple[float, float]] = []\n    for x, y in points:\n        if not math.isfinite(x) or not math.isfinite(y):\n            raise StarHistoryError(\"chart coordinates must be finite\")\n        if normalized and x < normalized[-1][0]:\n            raise StarHistoryError(\"chart coordinates must be ordered\")\n        if normalized and x == normalized[-1][0]:\n            normalized[-1] = (x, y)\n        else:\n            normalized.append((x, y))\n\n    if not normalized:\n        return \"\"\n\n    start_x, start_y = normalized[0]\n    commands = [f\"M{_format_float(start_x)},{_format_float(start_y)}\"]\n    if len(normalized) == 1:\n        return \"\".join(commands)\n    if len(normalized) == 2:\n        end_x, end_y = normalized[1]\n        commands.append(f\"L{_format_float(end_x)},{_format_float(end_y)}\")\n        return \"\".join(commands)","sourceCodeStart":768,"sourceCodeEnd":804,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L768-L804","documentation":"Raised in `_monotone_x_path` (scripts/star_history.py:786), the D3-curveMonotoneX-equivalent SVG path builder used by `render_svg`. Every (x, y) chart point must be a finite float; NaN or ±infinity cannot be serialized into valid SVG path coordinates and would poison the Steffen monotonic interpolation.","triggerScenarios":"`render_svg(state, theme)` reaching the path builder with a point whose x or y is NaN/inf. Points derive from `_chart_points(state)` (dates as x via matplotlib-style date numbers, stars as y) followed by axis scaling — non-finite values appear when state contains corrupt dates/stars that slipped past `validate_state`, or when custom code calls `_monotone_x_path` directly with computed coordinates that divide by zero (e.g. scale = 1/(max-min) with max==min).","commonSituations":"Hand-edited state JSON with nulls coerced to NaN upstream; a fork that changes chart scaling and introduces a zero-denominator; direct use of the private path helper in other tooling with unfiltered data.","solutions":["Run `validate_state(state)` on the exact state object before `render_svg` — the public renderer does this too, so a hit means the bad value appears only after scaling; check the state file for odd dates or star counts.","If you call `_monotone_x_path` directly, filter/replace non-finite points first: `pts = [(x, y) for x, y in pts if math.isfinite(x) and math.isfinite(y)]`.","In forked scaling code, guard degenerate ranges (`if x_max == x_min: x_max = x_min + 1`) so no inf emerges from division.","Rebuild the state from a fresh backfill/snapshot if the file was hand-edited."],"exampleFix":"# before: passing raw computed points\npath = _monotone_x_path(scaled_points)  # may contain nan/inf -> raises\n\n# after: sanitize before calling\nscaled_points = [(x, y) for x, y in scaled_points if math.isfinite(x) and math.isfinite(y)]\npath = _monotone_x_path(scaled_points)","handlingStrategy":"validation","validationCode":"import math\n\ndef finite_points(points):\n    return all(math.isfinite(x) and math.isfinite(y) for x, y in points)\n\nassert finite_points(scaled_points), \"chart math produced non-finite coordinates\"\npath = _monotone_x_path(scaled_points)","typeGuard":null,"tryCatchPattern":"try:\n    svg = render_svg(state, theme)\nexcept StarHistoryError as exc:\n    if \"must be finite\" in str(exc):\n        raise ValueError(\"state produced non-finite chart coordinates; regenerate state\") from exc\n    raise","preventionTips":["Only render state produced by the library's own builders — validate_state plus the chart pipeline keep coordinates finite by construction.","If you fork the axis scaling, add guards for degenerate ranges (max==min) that would divide by zero.","Never hand-edit star counts or dates in the state JSON to sentinel values like null/'NaN'."],"tags":["svg","chart","rendering","nan","internal-invariant"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}