Skip to content

Commit a5440fc

Browse files
committed
fix: Gemma4 worker — catch unhandled rejections + null-check Gemma4 classes
1 parent c1cbbdf commit a5440fc

2 files changed

Lines changed: 71 additions & 31 deletions

File tree

ai-worker-gemma4.js

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@ let processor = null;
2525
self.postMessage({ type: "status", message: `[Gemma4] Loading transformers.js ${TRANSFORMERS_URL.split('@').pop()}...` });
2626
console.log(`[Gemma4 worker] TRANSFORMERS_URL = ${TRANSFORMERS_URL}`);
2727

28+
// ── Global safety net ────────────────────────────────────────────────────────
29+
// Catch any uncaught async rejection (e.g. from the message handler's async fn)
30+
// and convert it to a postMessage error so the UI shows a readable status
31+
// instead of firing worker.onerror → "AI Worker error: Event".
32+
self.addEventListener('unhandledrejection', (event) => {
33+
const msg = event.reason && event.reason.message
34+
? event.reason.message
35+
: String(event.reason || 'Unknown error in Gemma4 worker');
36+
console.error('[Gemma4 worker] Unhandled rejection:', msg);
37+
self.postMessage({ type: 'error', message: `Gemma 4 worker error: ${msg}` });
38+
event.preventDefault();
39+
});
40+
2841
// ============================================
2942
// Progress callback factory
3043
// ============================================
@@ -73,6 +86,16 @@ async function loadModel() {
7386
read_audio = transformers.read_audio;
7487
TextStreamer = transformers.TextStreamer;
7588

89+
// ── Verify Gemma 4 classes are available in this version ──
90+
if (!Gemma4ForConditionalGeneration || !Gemma4Processor) {
91+
const ver = TRANSFORMERS_URL.split('@').pop();
92+
throw new Error(
93+
`Gemma 4 classes not found in transformers.js@${ver}. ` +
94+
`The loaded version does not support Gemma 4 yet. ` +
95+
`Try clearing site data and reloading.`
96+
);
97+
}
98+
7699
// 2. WebGPU detection
77100
let device = "wasm";
78101
if (typeof navigator !== "undefined" && navigator.gpu) {
@@ -238,36 +261,47 @@ async function generate({ userPrompt, prompt, attachments = [], context, chatHis
238261
self.addEventListener("message", async (event) => {
239262
const { type } = event.data;
240263

241-
switch (type) {
242-
case "setModelId":
243-
MODEL_ID = event.data.modelId || MODEL_ID;
244-
MODEL_LABEL = event.data.modelLabel || MODEL_LABEL;
245-
break;
246-
247-
case "load":
248-
await loadModel();
249-
break;
250-
251-
case "generate":
252-
await generate(event.data);
253-
break;
254-
255-
// Compatibility alias used by ai-docgen-generate.js
256-
case "process":
257-
await generate({
258-
prompt: event.data.prompt || event.data.task,
259-
attachments: event.data.attachments || [],
260-
context: event.data.context,
261-
messageId: event.data.messageId,
262-
options: event.data.options || {},
263-
});
264-
break;
265-
266-
case "ping":
267-
self.postMessage({ type: "pong" });
268-
break;
269-
270-
default:
271-
console.warn("Gemma4 worker — unknown message type:", type);
264+
try {
265+
switch (type) {
266+
case "setModelId":
267+
MODEL_ID = event.data.modelId || MODEL_ID;
268+
MODEL_LABEL = event.data.modelLabel || MODEL_LABEL;
269+
break;
270+
271+
case "load":
272+
await loadModel();
273+
break;
274+
275+
case "generate":
276+
await generate(event.data);
277+
break;
278+
279+
// Compatibility alias used by ai-docgen-generate.js
280+
case "process":
281+
await generate({
282+
prompt: event.data.prompt || event.data.task,
283+
attachments: event.data.attachments || [],
284+
context: event.data.context,
285+
messageId: event.data.messageId,
286+
options: event.data.options || {},
287+
});
288+
break;
289+
290+
case "ping":
291+
self.postMessage({ type: "pong" });
292+
break;
293+
294+
default:
295+
console.warn("Gemma4 worker — unknown message type:", type);
296+
}
297+
} catch (err) {
298+
// Catch synchronous throws from any case and route them as error messages
299+
// so they don't escape to worker.onerror (which shows generic "Model unavailable")
300+
console.error('[Gemma4 worker] Uncaught in message handler:', err);
301+
self.postMessage({
302+
type: 'error',
303+
message: `Gemma 4 worker error (${type}): ${err.message}`,
304+
messageId: event.data.messageId,
305+
});
272306
}
273307
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Gemma4 Worker — Unhandled Rejection Safety Net
2+
3+
- Added `unhandledrejection` listener at worker top level to route async throws to `postMessage({type:"error"})` instead of `worker.onerror`
4+
- Wrapped message handler switch in try/catch
5+
- Added null-checks for `Gemma4ForConditionalGeneration` + `Gemma4Processor` with readable error messages
6+
- Root cause: browser evicted cached model (persistent storage not granted), re-download triggered uncaught async rejection

0 commit comments

Comments
 (0)