Skip to content

Books page blank — unsupported Scan error on legacy date formats from Calibre import #914

@commiepinko

Description

@commiepinko

Pre-flight

  • I searched existing issues and this isn't a duplicate
  • I'm running the latest version (check Settings → About)

Current behaviour

The Books page shows "No books in your library yet" despite books existing in the database. All book list API calls fail immediately with a scan error. Authors page shows authors correctly; the failure is isolated to book queries.

Expected behaviour

Books page displays all books. Book list API calls return results.

Steps to reproduce

  1. Import a Calibre library into Bindery via the Calibre Bridge.
  2. Navigate to the Books page — it shows empty.
  3. Confirm books exist in the DB: sqlite3 bindery.db "SELECT COUNT(*) FROM books;" returns a non-zero count.
  4. Call the book list API directly:
    curl -s "http://localhost:8787/api/v1/book?page=1&pageSize=10" -H "X-Api-Key: <key>"

Root Cause
Calibre stores pubdate as 2006-01-02 15:04:05+00:00. When Bindery reads this and writes it to its own SQLite DB, Go serializes the time.Time value using its default time.String() format — 2006-01-02 15:04:05 +0000 UTC — rather than RFC3339. The modernc.org/sqlite driver cannot scan this format back into *time.Time, causing every book list query to fail with the error above.

Workaround
Run the following SQL against bindery.db while the container is stopped:

UPDATE books SET release_date = replace(replace(release_date, ' +0000 UTC', 'Z'), ' ', 'T') WHERE release_date LIKE '% +0000 UTC';
UPDATE books SET last_metadata_refresh_at = replace(replace(last_metadata_refresh_at, ' +0000 UTC', 'Z'), ' ', 'T') WHERE last_metadata_refresh_at LIKE '% +0000 UTC';
UPDATE books SET created_at = replace(replace(created_at, ' +0000 UTC', 'Z'), ' ', 'T') WHERE created_at LIKE '% +0000 UTC';
UPDATE books SET updated_at = replace(replace(updated_at, ' +0000 UTC', 'Z'), ' ', 'T') WHERE updated_at LIKE '% +0000 UTC';

Suggested Fix
Two changes to internal/db/books.go:

  1. Write side — wrap *time.Time parameters in a helper that formats them as RFC3339Nano before passing to the driver, preventing the bad format from being written going forward.
  2. Read side — scan all four time columns (release_date, last_metadata_refresh_at, created_at, updated_at) into sql.NullString and parse with a multi-layout fallback covering both RFC3339 and the legacy Go format. This makes existing databases with legacy date formats readable without requiring a migration.

Trace logs

{"error":"scan book: sql: Scan error on column index 8, name \"release_date\": unsupported Scan, storing driver.Value type string into type *time.Time"}

Deployment method

Docker (ghcr.io)

Bindery version

1.15.3

Environment details

  • OS: Unraid 7.2.7, kernel x86_64
  • Architecture: linux/amd64 (Intel i5-12600K)
  • Bindery version: sha-9560eba (via ghcr.io/vavallee/bindery:latest)
  • Deployment: Docker Compose
  • Calibre version: lscr.io/linuxserver/calibre:latest (KasmVNC-based)
  • Calibre library size: ~15,000 books, ~1,378 authors
  • Download clients: SABnzbd, qBittorrent (via VPN)
  • Indexers: NZBgeek, Nzb.life, altHUB (NZB); LimeTorrents, YTS, 1337x, EZTV, Nyaa.si, The Pirate Bay (torrent) — via Prowlarr
  • Reverse proxy: Nginx Proxy Manager
  • Calibre integration method: Bindery Bridge plugin running inside calibre-desktop container, port 8484

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds-triageNew issue, not yet reviewed

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions