Stars: single NULL star.updated row breaks all star reads/writes for that user (500 "expected stars object")
What happened?
For one specific user, every dashboard star operation fails: clicking the star icon returns HTTP 500, and the "Starred" section of the nav renders empty even though the user has valid star rows in the database. Other users on the same instance are unaffected.
Log output for the failing user:
logger=grafana-apiserver level=error msg="Unhandled Error" err="apiserver received an error that is not an metav1.Status: &errors.errorString{s:\"expected stars object\"}: expected stars object"
logger=context userId=2 level=error msg="Request Completed" method=POST path=/api/user/stars/dashboard/uid/business-kpis status=500 error="expected stars object"
Root cause
The star table schema declares updated as nullable (updated DATETIME NULL), but the collections API's legacy bridge scans it into a bare time.Time:
pkg/registry/apis/collections/legacy/sql.go, in getDashboardStars:
var updated time.Time
...
err := rows.Scan(&orgID, &userUID, &dashboardUID, &updated)
A single row with updated = NULL makes this Scan fail. Because sql_dashboard_stars.sql orders by s.updated asc and SQLite sorts NULLs first, the poisoned row is scanned first, so getDashboardStars fails for every call touching that user:
DashboardStarsStorage.Getreturns the scan error, which is not aNotFound- In
stars_update.go(starsREST.Connect), a non-NotFound error fromGetleavescurrentas nil, so thecurrent.(*collections.Stars)type assertion fails and the handler responds with the misleadingexpected stars objecterror for both PUT and DELETE - The read path fails the same way, so the user's starred list renders empty
Net effect: one NULL in a legally-nullable column bricks the entire stars feature for that user, including the DELETE path that could have removed the offending row via the API.
NULL values in this column occur in practice: rows written by older Grafana versions or by operators backfilling dashboard_uid during migrations (the column was added later; the schema accepts NULL, so nothing flags the insert).
Steps to reproduce
- Star any dashboard as a user
UPDATE star SET updated = NULL WHERE user_id = <that user>;- Reload Grafana: the starred list is empty
- Click the star icon on any dashboard: POST /api/user/stars/dashboard/uid/... returns 500 "expected stars object"
Suggested fix
Scan into sql.NullTime in getDashboardStars and fall back to a zero time (the value only affects star display order). Separately, stars_update.go swallows the underlying error when Get fails with a non-NotFound error; returning that error instead of the type-assertion message would have made this diagnosable from the log alone.
Environment
- Grafana 13.2.0 (f681b1359f6a0b8ecb9f2c49a88ac72b75bde73b), OSS, SQLite backend, deployed via kube-prometheus-stack
Source: grafana/grafana