Skip to content

fix(core): don't warn when tree-sitter highlight fails during client destroy - #1318

Closed
HARIOM-JHA01 wants to merge 1 commit into
anomalyco:mainfrom
HARIOM-JHA01:fix/treesitter-destroy-warn
Closed

HARIOM-JHA01 wants to merge 1 commit into
anomalyco:mainfrom
HARIOM-JHA01:fix/treesitter-destroy-warn

Conversation

@HARIOM-JHA01

@HARIOM-JHA01 HARIOM-JHA01 commented Aug 2, 2026 •

Copy link
Copy Markdown

Fixes #1319

Problem

When @opentui/core is torn down, TreeSitterClient.destroy() rejects any in-flight highlightOnce() promises with a generic Error("TreeSitter client destroyed"). CodeRenderable.startHighlight() treats every rejection as a real failure and logs a misleading warning:

Code highlighting failed, falling back to plain text: Error: ...

This races with client teardown on exit, so opencode users see the warning unexpectedly on shutdown even though nothing is actually broken.

Reported in anomalyco/opencode#36454.

Fix

  • client.ts: introduce a typed TreeSitterClientDestroyedError and reject pending requests (and pending initialization) with it in destroy().
  • Code.ts: silently bail out when highlighting fails due to client destroy (or the renderable is already destroyed), preserving warnings for genuine worker failures.
  • Testing: mock-tree-sitter-client supports rejectHighlightOnce; client destroy tests now assert TreeSitterClientDestroyedError;
  • Code renderer tests verify no console.warn on destroy-triggered failures while a real Error still warns.

Tests

  • bun test packages/core/src/lib/tree-sitter/client.test.ts (49 pass)
  • oxlint / oxfmt clean
  • Renderer tests (Code.test.ts) require the native lib and run in CI.

…destroy

The TreeSitterClient used a generic Error for teardown rejections, so CodeRenderable treated an expected destroy() during shutdown as a real failure and logged 'Code highlighting failed, falling back to plain text'. Introduce TreeSitterClientDestroyedError and skip the warning (and pointless fallback render) when highlighting fails because the client was destroyed, while preserving warnings for genuine worker failures.
@jizhi0v0

Copy link
Copy Markdown

Still reproducing on opencode 1.18.18 (macOS, arm64). That tag pins @opentui/core 0.4.5, and the shipped bundle still has the console.warn ahead of the guard:

$ strings -a ~/.opencode/bin/opencode | grep -o 'Code highlighting failed.\{0,120\}'
Code highlighting failed, falling back to plain text:",H),this.isDestroyed)return;this.textBuffer.setText($)...

Triggered by Ctrl+C during a long session. The warning repeats once per pending highlight request — destroy() → rejectPendingRequests() rejects the entire messageCallbacks map in one go, so the longer the transcript, the more copies get dumped. Teardown stack:

at destroy (chunk-hfnzvvc7.js:64:11892)          <- TreeSitterClient.destroy
at LZ (chunk-hfnzvvc7.js:64:19205)
at finalizeDestroy (chunk-hfnzvvc7.js:93:22271)
at destroy (chunk-hfnzvvc7.js:93:19087)
at gg (chunk-5haa5ka2.js:94:18044)
at exit (chunk-5haa5ka2.js:94:20824)
...
at processParsedKey (chunk-hfnzvvc7.js:26:9634)
at handleStdinEvent (chunk-hfnzvvc7.js:93:5055)

One detail worth noting: it lands on the raw terminal rather than the TUI console pane, because finalizeDestroy() calls _console.destroy() → restoreOriginalConsole() in the same synchronous block, while the rejections settle on a later microtask — so by the time the warns fire, global.console is already the original one.

The typed-TreeSitterClientDestroyedError approach here looks like the right call. Guarding on isDestroyed alone wouldn't cover it, since the rejection can land on a CodeRenderable that hasn't been destroyed itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Code highlighting failed, falling back to plain text warning logged on exit (destroy race)

3 participants