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

  1. Use one of the supported routes: /opds, /opds/books, /opds/search, /opds/search.xml, /opds/book/{key}, /opds/cover/{key}, /opds/download/{key}.{format}.
  2. Start from the root catalog /opds and follow the hrefs it emits instead of hand-constructing URLs.
  3. Check for typos and case sensitivity in the path; Go matching here is exact and case-sensitive.
  4. 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

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


AI-assisted analysis of koodo-reader/koodo-reader@7d40df41e0 (2026-08-29). Data as JSON: /api/errors/fe8377ea7a01f1ae. Report an issue: GitHub.