Summary
When an app using @opentui/core exits, TreeSitterClient.destroy() rejects any in-flight highlightOnce() requests. CodeRenderable treats those rejections as genuine highlighting failures and logs a misleading warning:
Code highlighting failed, falling back to plain text: Error: TreeSitter client destroyed
This occurs during normal shutdown, so nothing is actually wrong. As a result, applications such as opencode print this warning on every exit (see anomalyco/opencode#36454).
Reproduction
- Run any OpenTUI app that renders a
CodeRenderable (or markdown containing code).
- Exit the application while syntax highlighting is still in progress (e.g.
Ctrl+C or /exit).
- Observe the warning printed after teardown.
Root Cause
During shutdown, TreeSitterClient.destroy() rejects all pending highlight requests with:
new Error("TreeSitter client destroyed")
CodeRenderable.startHighlight() catches every rejected highlightOnce() call and assumes it represents a real highlighting failure, logging:
console.warn(
"Code highlighting failed, falling back to plain text:",
error,
);
However, in this case the rejection is an expected consequence of teardown, not an actual error.
Expected Behavior
Shutdown-related cancellations should be treated as expected and should not:
- emit a warning, or
- trigger the normal highlighting failure path.
Actual highlighting failures (for example, parser initialization or worker errors) should continue to log warnings so genuine issues remain visible.
Summary
When an app using
@opentui/coreexits,TreeSitterClient.destroy()rejects any in-flighthighlightOnce()requests.CodeRenderabletreats those rejections as genuine highlighting failures and logs a misleading warning:This occurs during normal shutdown, so nothing is actually wrong. As a result, applications such as
opencodeprint this warning on every exit (see anomalyco/opencode#36454).Reproduction
CodeRenderable(or markdown containing code).Ctrl+Cor/exit).Root Cause
During shutdown,
TreeSitterClient.destroy()rejects all pending highlight requests with:CodeRenderable.startHighlight()catches every rejectedhighlightOnce()call and assumes it represents a real highlighting failure, logging:However, in this case the rejection is an expected consequence of teardown, not an actual error.
Expected Behavior
Shutdown-related cancellations should be treated as expected and should not:
Actual highlighting failures (for example, parser initialization or worker errors) should continue to log warnings so genuine issues remain visible.