Symptom. GET /api/v1/observers/{id}/adverts?limit=50 returns 500 for some observers (seen on analyzer.meshmapper.net for at least four observers on 2026-09-02/03). The app log has the cause:
api: ListObserverAdverts failed: can't scan into dest[9] (col: node_public_key): cannot scan NULL into *string
api: error 500: internal server error
Cause. db/queries/queries.sql:263-284 (ListObserverAdverts) selects encode(p.origin_pubkey, 'hex') AS node_public_key. sqlc types that column as a non-null string, but packets.origin_pubkey can be NULL, so any advert row without an origin pubkey breaks the whole page.
Fix options.
COALESCE(encode(p.origin_pubkey, 'hex'), '') AS node_public_key, or
- make the column nullable in the generated struct (
::text + sqlc.narg/pgtype.Text) and expose it as *string in the API type, or
AND p.origin_pubkey IS NOT NULL if such rows are never meaningful as adverts.
Whichever is chosen, a NULL in one row should not fail the endpoint. A regression test with one NULL-pubkey advert would catch this.
Symptom.
GET /api/v1/observers/{id}/adverts?limit=50returns 500 for some observers (seen on analyzer.meshmapper.net for at least four observers on 2026-09-02/03). The app log has the cause:Cause.
db/queries/queries.sql:263-284(ListObserverAdverts) selectsencode(p.origin_pubkey, 'hex') AS node_public_key. sqlc types that column as a non-nullstring, butpackets.origin_pubkeycan be NULL, so any advert row without an origin pubkey breaks the whole page.Fix options.
COALESCE(encode(p.origin_pubkey, 'hex'), '') AS node_public_key, or::text+sqlc.narg/pgtype.Text) and expose it as*stringin the API type, orAND p.origin_pubkey IS NOT NULLif such rows are never meaningful as adverts.Whichever is chosen, a NULL in one row should not fail the endpoint. A regression test with one NULL-pubkey advert would catch this.