ML-171: research and plan

This commit is contained in:
Claudio Ortolina
2026-05-09 06:29:38 +01:00
parent 2c0e0e3fae
commit f8780f3582
@@ -0,0 +1,72 @@
---
id: ML-171
title: >-
Fix Oban Web crons page crash: MatchError in sparkline from SQLite datetime
string
status: To Do
assignee: []
created_date: "2026-05-09 05:28"
labels:
- bug
- oban-web
- sqlite
dependencies: []
references:
- >-
https://github.com/oban-bg/oban_web/blob/main/lib/oban/web/live/crons/table_component.ex#L78-L85
- >-
https://github.com/oban-bg/oban_web/blob/main/lib/oban/web/queries/cron_query.ex#L164-L177
modified_files:
- lib/music_library_web/live/oban/crons/table_component.ex
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
## Summary
The `/dev/oban/crons` dashboard page crashes with `Elixir.MatchError` when rendering cron job history sparklines. This happens because Oban Web's `TableComponent.sparkline/1` calls `DateTime.from_naive!/2` which expects a `NaiveDateTime` struct, but receives a raw ISO 8601 string from SQLite.
## Root Cause
In `Oban.Web.CronQuery.cron_history/2` (deps/oban_web/lib/oban/web/queries/cron_query.ex:164-177), the query selects:
```elixir
finished_at: fragment("COALESCE(?, ?, ?)", j.completed_at, j.cancelled_at, j.discarded_at)
```
SQLite stores `utc_datetime_usec` fields as ISO 8601 text. While regular field references like `j.scheduled_at` are properly decoded to `DateTime` structs by Ecto, the `fragment()` call bypasses type decoding and returns a raw string like `"2026-05-08T11:00:00.708342Z"`.
The sparkline function at `Oban.Web.Crons.TableComponent` line 81 then fails:
```elixir
(job.finished_at || job.attempted_at || job.scheduled_at)
|> DateTime.from_naive!("Etc/UTC") # 💥 expects NaiveDateTime, gets string
```
## Error Details
- **ErrorTracker ID**: 4039
- **Occurrences**: 2 (both on 2026-05-08)
- **View**: Oban.Web.DashboardLive (crons page)
- **Fingerprint**: `2C5253F1AB9B5D61231B7579E912FD2F7C155738DBB4325275F14E844C5D7B0C`
## Approach
The `sparkline/1` function is marked as `@doc overridable: 1` in Oban Web, so we can override it in the app. The fix should handle both `DateTime` structs (PostgreSQL) and ISO 8601 strings (SQLite) by parsing strings through `DateTime.from_iso8601!/1` first.
Alternatively, the fix could be applied upstream in Oban Web's `CronQuery` to use a typed fragment or cast the `COALESCE` result.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Visiting /dev/oban/crons no longer crashes with MatchError
- [ ] #2 Cron history sparklines render correctly showing job state colors
- [ ] #3 The fix handles both DateTime structs (PostgreSQL) and ISO 8601 strings (SQLite)
- [ ] #4 The overridden sparkline/1 function preserves the same visual output as upstream
<!-- AC:END -->