POST /api/queue/cancel answers 404 {"error":"hash not in queue"} for
untracked hashes instead of 200 ok:true. The UI surfaces the server's
error message instead of swallowing it.
2.6 KiB
type, title, description, tags, timestamp
| type | title | description | tags | timestamp | ||||
|---|---|---|---|---|---|---|---|---|
| API | Web UI JSON API | The UI-facing JSON routes served by src/ui-api.js (search, queue add/list/cancel), the 404-on-cancel contract, and the UI error surfacing. |
|
2026-10-19T00:00:00Z |
Web UI JSON API
The Web UI talks to the app over four JSON routes registered by registerUiApi(app, { qbt, client }) in src/ui-api.js, which src/index.js mounts before static file serving so /api/* is never swallowed. __tests__/ui-api.test.js covers all four routes.
| Route | Success | Error |
|---|---|---|
GET /api/search?q=… |
200 result array ([] when q is empty) |
500 {error} when the Webshare client fails |
POST /api/queue/add |
200 {ok: true} |
400 {error: 'ident required'}, 409 {error: 'already in queue'}, 500 {error} |
GET /api/queue |
200 queue array |
500 {error} |
POST /api/queue/cancel |
200 {ok: true} |
400 {error: 'hash required'}, 404 {error: 'hash not in queue'} when the hash is not in the queue, 500 {error} |
Cancel returns 404 for unknown hashes
POST /api/queue/cancel honors the return value of qbt.cancelDownload(hash) (see Project Overview), which is a boolean: false means no queued download matched the hash, and the route responds 404 {error: 'hash not in queue'}. Before this, the route always answered 200 {ok: true} even when nothing was cancelled.
The 404 body message is deliberately human-readable — the UI surfaces error verbatim (below), and a raw body read is still intelligible.
The UI surfaces the error
apiFetch in ui/scripts.js parses a failed response's JSON body when content-type is application/json and throws new Error(data.error) (falling back to the raw text or HTTP <status> for non-JSON or malformed bodies). The cancel-button handler then shows the failure inline as a transient [err] button state (re-enabled after 2 s) instead of swallowing it in catch {}; the 2 s queue poll repaints the row. This mirrors the established inline-error pattern of the add button — the codebase has no toasts or alerts.
The fake qBittorrent API is intentionally different
The fake qBittorrent Web API in src/qbt.js (POST /api/v2/torrents/delete and friends) was not changed: it answers Ok. for real qBittorrent-API clients such as Sonarr, whose contract is always Ok., and Sonarr deletes finished entries — a 404 there would break it. The two APIs serve different consumers and keep different error contracts on purpose.
Related
- Project Overview — module layout and build/test commands.