Summary
plugins/tps-meter.tsx wraps virtually every event handler, reactive computation, and side effect in silent catch {} blocks. While the stated intent is "defensive — fail closed," the implementation catches all error types including TypeError, ReferenceError, and programming bugs. This makes runtime debugging of the plugin nearly impossible because legitimate failures are silently swallowed with zero visibility — no console, no fallback UI, nothing.
Affected Code
File: plugins/tps-meter.tsx
| Location |
Lines |
What's caught silently |
onPart handler |
117-119 |
Malformed events, but also property access errors, type errors |
onMessage handler |
135-137, 145-146 |
Parts API failures, but also null deref, type errors |
onMessageRemoved |
160-162 |
Any error in map delete / property access |
onPartRemoved |
169-171 |
Any error in map delete |
| Event bus subscription loop |
185-186 |
Event type not supported |
| Top-level event bus access |
190-192 |
No event bus at all |
| Interval tick |
197-199 |
RateMeter.sample failures |
| Cleanup off handlers |
206-208 |
Cleanup call failures |
| View compute (createMemo) |
218-222 |
State.session.messages lookup failures |
| Session status lookup |
237-240 |
Status API failures |
tui() registration |
314-316 |
Any registration failure |
That's 11 catch blocks across a single 319-line file, and 10 of them are empty catch {} or catch { /* ignore */ }.
Why This Is a Problem
The architectural principle ("fail closed, render nothing") is sound. But the implementation conflates two very different failure modes:
- Expected / recoverable: The OpenCode runtime is a different version and the API shape changed (API drift). The plugin should indeed render nothing.
- Unexpected / bugs: A misspelled variable name, a logic error in the handler body, or a SolidJS reactivity violation. These should be visible to the developer during development and testing.
By catching all errors identically, bug #2 is indistinguishable from scenario #1. A developer testing a change to the onMessage handler would see "nothing renders" with no hint that they have a TypeError on a specific line.
Concrete Example
If a developer accidentally writes:
const onMessage = (event) => {
try {
const info = event?.properties?.info;
if (info && info.sessionID === props.sessionID && isAssistant(info) && info.time?.completed) {
const generated = (Number(info.tokens?.ouput) || 0) + ... // BUG: "ouput" not "output"
The catch {} on line 145 silently swallows the resulting NaN propagation / undefined behavior. The developer sees "the TPS section disappeared" with zero diagnostic information.
Recommended Fix
Use a guarded error boundary pattern: catch only the specific errors that indicate API drift (missing properties, wrong method signatures), and let unexpected errors propagate during development while still failing closed in production.
At minimum, log unexpected errors to console.warn or console.debug when process.env.NODE_ENV !== "production":
catch (err) {
if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production") {
console.warn("[opencode-tps-meter] handler error:", err);
}
}
Even better, check the specific error type:
catch (err) {
// API drift: an expected property or method is missing → fail closed.
if (err instanceof TypeError && /is not a function|undefined|read properties/.test(err.message)) {
return; // API shape changed — expected, render nothing.
}
// Unexpected error: log it so developers can see the real problem.
console.warn("[opencode-tps-meter] unexpected error:", err);
}
Related
- The
verify-plugin tool (tools/verify-plugin.mjs) tries to detect some errors by checking for "renderer" in the error message during component construction — but this only catches one specific error class, and only when Bun is available with peer deps installed.
Summary
plugins/tps-meter.tsxwraps virtually every event handler, reactive computation, and side effect in silentcatch {}blocks. While the stated intent is "defensive — fail closed," the implementation catches all error types includingTypeError,ReferenceError, and programming bugs. This makes runtime debugging of the plugin nearly impossible because legitimate failures are silently swallowed with zero visibility — no console, no fallback UI, nothing.Affected Code
File:
plugins/tps-meter.tsxonParthandleronMessagehandleronMessageRemovedonPartRemovedtui()registrationThat's 11 catch blocks across a single 319-line file, and 10 of them are empty
catch {}orcatch { /* ignore */ }.Why This Is a Problem
The architectural principle ("fail closed, render nothing") is sound. But the implementation conflates two very different failure modes:
By catching all errors identically, bug #2 is indistinguishable from scenario #1. A developer testing a change to the
onMessagehandler would see "nothing renders" with no hint that they have aTypeErroron a specific line.Concrete Example
If a developer accidentally writes:
The
catch {}on line 145 silently swallows the resultingNaNpropagation / undefined behavior. The developer sees "the TPS section disappeared" with zero diagnostic information.Recommended Fix
Use a guarded error boundary pattern: catch only the specific errors that indicate API drift (missing properties, wrong method signatures), and let unexpected errors propagate during development while still failing closed in production.
At minimum, log unexpected errors to
console.warnorconsole.debugwhenprocess.env.NODE_ENV !== "production":Even better, check the specific error type:
Related
verify-plugintool (tools/verify-plugin.mjs) tries to detect some errors by checking for "renderer" in the error message during component construction — but this only catches one specific error class, and only when Bun is available with peer deps installed.