koodo-reader/koodo-reader · error
Not Found
Error message
Not Found
What it means
The OPDS router in opdsHandler fell through all known route cases and hit the default branch, which answers 404 Not Found. This means the request path under /opds did not exactly match the root/books/search routes nor start with the book/, cover/, or download/ prefixes.
Source
Thrown at httpserver/opds.go:604
handleOPDSCover(w, r, key)
// Book file download: /opds/download/{key}.{format}
case strings.HasPrefix(path, "/opds/download/"):
filename := strings.TrimPrefix(path, "/opds/download/")
filename = strings.Trim(filename, "/")
// Strip extension to get key
key := filename
if idx := strings.LastIndex(filename, "."); idx > 0 {
key = filename[:idx]
}
if key == "" {
http.Error(w, "Missing book key", http.StatusBadRequest)
return
}
handleOPDSDownload(w, r, key)
default:
http.Error(w, "Not Found", http.StatusNotFound)
}
}
View on GitHub (pinned to 7d40df41e0)
Solutions
- Use one of the supported routes: /opds, /opds/books, /opds/search, /opds/search.xml, /opds/book/{key}, /opds/cover/{key}, /opds/download/{key}.{format}.
- Start from the root catalog /opds and follow the hrefs it emits instead of hand-constructing URLs.
- Check for typos and case sensitivity in the path; Go matching here is exact and case-sensitive.
- If the server is behind a reverse proxy or sub-path, ensure the proxy strips the prefix so requests reach the router as /opds/... .
Example fix
// before GET /opds/book/abc/extra -> if unmatched variant like GET /opds/Books // after GET /opds/books # exact, lowercase, no trailing slash
Defensive patterns
Strategy: validation
Validate before calling
const OPDS_ROUTES = /^\/opds(\/books|\/search(\.xml)?|\/book\/[^/]+|\/cover\/[^/]+|\/download\/[^/]+\.[^/]+)?\/?$/;
function isKnownOpdsPath(p) { return OPDS_ROUTES.test(p); } Type guard
function isOpdsPath(v) {
return typeof v === 'string' && v.startsWith('/opds') && isKnownOpdsPath(v);
} Try / catch
const res = await fetch(path);
if (res.status === 404) {
throw new Error(`Unknown OPDS endpoint: ${path}. Valid: /opds, /opds/books, /opds/search, /opds/search.xml, /opds/book/{key}, /opds/cover/{key}, /opds/download/{key}.{format}`);
}
if (!res.ok) throw new Error(`OPDS request failed: ${res.status}`); Prevention
- Discover endpoints by parsing hrefs from the root /opds catalog instead of hardcoding paths.
- Keep paths lowercase and exact — matching is case-sensitive and trailing-segment sensitive.
- If reverse-proxied under a sub-path, configure the proxy to rewrite to /opds/... before the server.
When it happens
Trigger: Any authenticated GET to an unmatched /opds path, e.g. /opds/book (no trailing slash/segment... actually matches prefix — rather: /opds/unknown, /opds/Book/xyz (case-sensitive), /opds/books/, /opds/search?q=... with a different verb path like /opds/feed/123, or a typo such as /opds/boo/abc.
Common situations: Typo'd endpoints in OPDS client config (e.g. /opds/bookx/), case-sensitive path mismatches, older clients using a removed/renamed route, proxies or base-path rewrites that alter the URL so prefixes no longer match (e.g. serving under /reader/opds).
Related errors
- HTTP ${response.status}: ${response.statusText}
- HTTP ${response.status}
- Book not found
- Missing book key
- Failed to download file: ${response.statusText}
AI-assisted analysis of koodo-reader/koodo-reader@7d40df41e0 (2026-08-29).
Data as JSON: /api/errors/fe8377ea7a01f1ae.
Report an issue: GitHub.