Skip to content

Commit 2396286

Browse files
brianckeeganclaude
andauthored
Enrich docling-serve error messages with workaround hints (#37)
When a docling-serve response contains a known failure pattern, append a short actionable hint to the message before it surfaces in the plugin's toast. Today: one pattern, the Apple-Silicon MPS + float64 RT-DETRv2 bug. Tomorrow: easy to add more patterns to the same table. A real user hit "HTTP 500: Cannot convert a MPS Tensor to float64 dtype …" with no recovery path from the toast itself. With this change the toast also says: "Apple Silicon MPS bug — restart docling-serve with PYTORCH_ENABLE_MPS_FALLBACK=1 (see README → Troubleshooting)." The README change in the previous PR (docs/troubleshooting-mps) owns the long-form explanation; this PR is just the in-app pointer. Design - New `src/utils/serverErrorHints.ts` exports a `KNOWN_SERVER_ISSUES` table (`{id, matches, hint}[]`) and a pure `enrichServerError(message)` helper. The helper appends one hint per matching entry, separated by " · ". No match → unchanged message. Predicate throws are swallowed — a broken pattern must never break error surfacing. - The MPS pattern matches both "MPS" and "float64" as independent tokens rather than as a fixed phrase, so it survives reorderings across docling / transformers / PyTorch versions. - Wired into convert.ts at the two outbound-error sites: * parseConvertResponse — non-2xx with body. * convertAttachmentInner — server-side "failure"/"partial_success". Both apply the helper just before returning the message that becomes the toast. Tests - New `test/serverErrorHints.test.ts` follows the Goldberg conventions already in place (3-part names, AAA blocks, BDD assertions, `#cold` tag, realistic input verbatim from a docling-serve traceback). - Covers: MPS+float64 match (both orderings), unrelated errors pass through unchanged, empty input, single-token false-positive cases (MPS without float64, float64 without MPS — neither triggers). - Sanity-checks the table itself (every entry has id/matches/hint; ids are unique). `npx tsc --noEmit`, `npx eslint .`, `npx prettier --check .`, and `npm run build` all pass. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c31714a commit 2396286

3 files changed

Lines changed: 75 additions & 2 deletions

File tree

src/modules/convert.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
stripExistingFrontmatter,
1818
} from "../utils/frontmatter";
1919
import { withDbLock } from "../utils/dbLock";
20+
import { enrichServerError } from "../utils/serverErrorHints";
2021
import { toast } from "./ui";
2122

2223
const LOG = "[zotero-docling]";
@@ -369,7 +370,8 @@ async function parseConvertResponse(r: Response): Promise<FetchOutcome> {
369370
if (!r.ok) {
370371
const errs = formatServerErrors(data);
371372
const hasDetail = errs && !errs.startsWith("status=");
372-
return { ok: false, message: hasDetail ? `${label}: ${errs}` : label };
373+
const raw = hasDetail ? `${label}: ${errs}` : label;
374+
return { ok: false, message: enrichServerError(raw) };
373375
}
374376
return { ok: true, data };
375377
}
@@ -653,9 +655,10 @@ async function convertAttachmentInner(
653655
const okStatus =
654656
data.status === "success" || data.status === "partial_success";
655657
if (!okStatus) {
658+
const raw = `Conversion ${data.status ?? "unknown"}: ${formatServerErrors(data)}`;
656659
return {
657660
status: "error",
658-
message: `Conversion ${data.status ?? "unknown"}: ${formatServerErrors(data)}`,
661+
message: enrichServerError(raw),
659662
};
660663
}
661664
const rawMarkdown = data.document?.md_content;

src/utils/serverErrorHints.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Recognise known shapes of docling-serve / Docling / upstream-library
2+
// failure modes in raw error strings, and append a one-line actionable
3+
// hint to the message.
4+
//
5+
// Why this exists: the plugin's failure toast historically surfaces the
6+
// server's verbatim error ("HTTP 500: Cannot convert a MPS Tensor to
7+
// float64 dtype…"). For known patterns we can do better — append a
8+
// short hint pointing at the workaround, so the user doesn't have to
9+
// search the README to recover. The README still owns the long-form
10+
// explanation; this module just points there.
11+
//
12+
// Patterns are evaluated in order. Each matching pattern appends its
13+
// hint once. The function is pure and lives outside convert.ts so it
14+
// can be unit-tested without the Zotero global.
15+
16+
interface KnownIssue {
17+
/** Short identifier — surfaced in Zotero.debug logs but not the toast. */
18+
id: string;
19+
/**
20+
* Predicate. A RegExp is cheapest; for multi-token AND-matching where
21+
* order varies, prefer a function (see the MPS entry below).
22+
*/
23+
matches: (message: string) => boolean;
24+
/** One-line hint appended after a separator. Keep this short. */
25+
hint: string;
26+
}
27+
28+
// Order matters only insofar as a single match emits a single hint; if a
29+
// caller ever wants multiple hints from one message, they'll accumulate
30+
// in the order declared here.
31+
export const KNOWN_SERVER_ISSUES: ReadonlyArray<KnownIssue> = [
32+
{
33+
id: "mps-float64",
34+
// RT-DETRv2 in transformers hard-codes a float64 tensor; PyTorch's
35+
// MPS backend can't represent float64 on Apple Silicon. The two
36+
// tokens can appear in either order across versions, so match each
37+
// independently rather than as a fixed phrase.
38+
matches: (m) => /\bMPS\b/.test(m) && /\bfloat64\b/i.test(m),
39+
hint: "Apple Silicon MPS bug — restart docling-serve with PYTORCH_ENABLE_MPS_FALLBACK=1 (see README → Troubleshooting).",
40+
},
41+
];
42+
43+
/**
44+
* Append known-issue hint(s) to a server error message. If no pattern
45+
* matches, returns the message unchanged. Multiple matching patterns
46+
* each append their hint, separated by " · ".
47+
*
48+
* Examples:
49+
*
50+
* enrichServerError("HTTP 500: Cannot convert a MPS Tensor to float64 dtype")
51+
* → "HTTP 500: Cannot convert a MPS Tensor to float64 dtype
52+
* · Apple Silicon MPS bug — restart docling-serve with
53+
* PYTORCH_ENABLE_MPS_FALLBACK=1 (see README → Troubleshooting)."
54+
*
55+
* enrichServerError("Server not reachable")
56+
* → "Server not reachable" (unchanged — no known issue matched)
57+
*/
58+
export function enrichServerError(message: string): string {
59+
if (!message) return message;
60+
const hints: string[] = [];
61+
for (const issue of KNOWN_SERVER_ISSUES) {
62+
try {
63+
if (issue.matches(message)) hints.push(issue.hint);
64+
} catch {
65+
// A broken predicate must never break error surfacing.
66+
}
67+
}
68+
if (hints.length === 0) return message;
69+
return `${message}\n· ${hints.join("\n· ")}`;
70+
}

test/serverErrorHints.test.ts

5.37 KB
Binary file not shown.

0 commit comments

Comments
 (0)