Skip to content

Discogs release_year=0 leaks through metadata pipeline as literal 0 #1002

@jakebromberg

Description

@jakebromberg

Problem

Discogs returns release_year: 0 as a sentinel for "year unknown" on releases without a verified release date. Backend-Service's metadata pipeline passes this 0 through unchanged in two places:

  1. Runtime pathextractAlbumMetadata at apps/backend/services/metadata/metadata.service.ts:114: releaseYear: artwork.release_year ?? undefined?? only fires on null/undefined, so the 0 leaks straight through to the wire response served to the iOS playcut detail view and is persisted to flowsheet.release_year via enrichment.service.ts:112 as a literal 0.

  2. Historical backfill pathjobs/flowsheet-metadata-backfill/enrich.ts:152: same ?? null shape on the direct DB write.

Discovered while investigating a 2026-05-22 user-reported iOS bug: an Autechre/ilanders playcut detail view rendered Release year: 0 along with no label and a band-image fallback. Looking at the row, release_year: 0 was written by the runtime enrichment from a Discogs cold-cache lookup that matched release 2316777 (Oversteps, which has Discogs year 0 because the source release lacks a verified date).

Blast radius

SELECT count(*) FROM wxyc_schema.flowsheet WHERE release_year = 0;
-- 11824

11,824 existing prod flowsheet rows carry release_year=0 today and render literal 0 on the iOS playcut detail view. Every cold-cache lookup against a year-less Discogs release adds one. The Post-launch hardening project (#32) is exactly the bucket for this kind of quality leak in recently-launched live services.

Proposed approach

One-character fix at each of the two call sites, plus a one-shot data backfill:

  1. metadata.service.ts:114 — change releaseYear: artwork.release_year ?? undefinedreleaseYear: artwork.release_year || undefined. || coerces 0 (and any other falsy) to undefined. Year-0 has no real-world meaning for music releases, so the broader-than-?? coercion is safe.

  2. jobs/flowsheet-metadata-backfill/enrich.ts:152 — change release_year: artwork.release_year ?? nullrelease_year: artwork.release_year || null. Same shape.

  3. One-shot UPDATE to clean up the existing 11,824 rows:

    UPDATE wxyc_schema.flowsheet SET release_year = NULL WHERE release_year = 0;

    Single-statement, fast, idempotent. Run after the code fix lands so any concurrent enrichment writes can't reintroduce 0. Doesn't need a migration file because the schema doesn't change — just a one-time SQL ticked off in the PR description.

Acceptance criteria

  • metadata.service.ts: when LML returns release_year: 0, the resulting AlbumMetadataResult.releaseYear is undefined (covered by a new unit test in tests/unit/services/metadata.service.test.ts)
  • enrich.ts: when LML returns release_year: 0, the row update writes NULL (covered by an enrich.test.ts test if one exists alongside, or added)
  • One-shot UPDATE clearing existing 11,824 release_year = 0 rows to NULL is documented in the PR description and ticked-off after merge
  • No new test for artwork.release_year = null is needed (existing behavior — already null-coerced); existing tests covering 2001-style years stay green

Constraints

  • The fix must not erase any legitimate year-zero entries — but year 0 AD has no music releases, so there's no real-world data being thrown away. (1 BC = year 0 in ISO 8601 but Discogs doesn't tag releases that way.)
  • Must run after the code fix lands so concurrent writes don't reintroduce 0 immediately.
  • No schema change required.

Related

  • Original investigation: 2026-05-22 iOS playcut-detail report on Autechre/ilanders. The duplicate-row half of that incident is filed at WXYC/tubafrenzy#555; this issue covers the orthogonal metadata-quality half.
  • Fits under Post-launch service hardening (epic G — observability + epic D — album_metadata) — adjacent to the broader metadata-quality work but a small, self-contained piece.
  • Shares the "inline-copy parity" pattern documented around filterSpacerGif (metadata.service.ts:101 + scripts/check-spacer-gif-callsites.sh) — same two-call-site coercion shape.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinglmlTouches library-metadata-lookup

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions