{"record":{"id":"4e8011b17fea391d","repo":"mozilla/pdf.js","slug":"app-thermometer-is-read-only","errorCode":null,"errorMessage":"app.thermometer is read-only","messagePattern":"app\\.thermometer is read-only","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/scripting_api/app.js","lineNumber":353,"sourceCode":"    return this._runtimeHighlightColor;\n  }\n\n  set runtimeHighlightColor(val) {\n    if (Color._isValidColor(val)) {\n      this._runtimeHighlightColor = val;\n      /* TODO */\n    }\n  }\n\n  get thermometer() {\n    return (this._thermometer ??= new Proxy(\n      new Thermometer({ send: this._send }),\n      this._proxyHandler\n    ));\n  }\n\n  set thermometer(_) {\n    throw new Error(\"app.thermometer is read-only\");\n  }\n\n  get toolbar() {\n    return this._toolbar;\n  }\n\n  set toolbar(val) {\n    this._toolbar = val;\n    /* TODO */\n  }\n\n  get toolbarHorizontal() {\n    return this.toolbar;\n  }\n\n  set toolbarHorizontal(value) {\n    /* has been deprecated and it's now equivalent to toolbar */\n    this.toolbar = value;","sourceCodeStart":335,"sourceCodeEnd":371,"githubUrl":"https://github.com/mozilla/pdf.js/blob/5903d58d58e4dd9ce6ffa3834aea8480f06b4ada/src/scripting_api/app.js#L335-L371","documentation":"app.thermometer is a lazily-created Proxy around a Thermometer progress helper that communicates via the send callback. It is viewer-internal state, so the setter throws. Reassigning would detach the progress UI wiring.","triggerScenarios":"Any assignment app.thermometer = ... in PDF JavaScript or API test code hits the throw at src/scripting_api/app.js:353.","commonSituations":"Long-running scripts trying to install a custom progress object; tests attempting to mock progress by overwriting the property.","solutions":["Use the returned Thermometer proxy's API (begin/update/end) instead of reassigning app.thermometer.","For tests, mock at the underlying _externalCall/send layer.","Read app.thermometer to obtain the current progress object."],"exampleFix":"// before\napp.thermometer = myProgress;\n// after\nconst t = app.thermometer; t.begin(); t.update(50);","handlingStrategy":"validation","validationCode":"// app.thermometer is a viewer-managed progress proxy; use begin/update/end.\nfunction runWithProgress(app, fn) {\n  const t = app.thermometer;\n  if (!t || typeof t.begin !== 'function') throw new Error('Thermometer unavailable');\n  t.begin(); try { return fn(t); } finally { t.end(); }\n}","typeGuard":"const isThermometer = (v) =>\n  v != null && typeof v === 'object' && typeof v.begin === 'function';","tryCatchPattern":"try { /* code that may assign app.thermometer */ }\ncatch (e) { if (!/thermometer is read-only/.test(e.message)) throw e; }","preventionTips":["Operate on the returned proxy; never replace app.thermometer.","Mock progress at the _externalCall layer in tests.","Always pair begin() with end() to keep the UI consistent."],"tags":["scripting-api","read-only","progress","proxy"],"backgroundTag":null,"analyzedSha":"5903d58d58e4dd9ce6ffa3834aea8480f06b4ada","analyzedAt":"2026-08-13T02:28:27.364Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}