In canvas@4.0.0-rc1 ( PR #2602 ), passing an empty string to any of the text APIs, ctx.measureText(""), ctx.fillText("", x, y), or ctx.strokeText("", x, y), crashes the entire Node.js process with SIGTRAP (exit code 133). It's a native abort, not a JS exception, so it cannot be caught with try/catch and takes down the whole application.
In v3.x (and in browsers), these calls are valid no-ops: measureText("") returns metrics with width: 0, and fillText("")/strokeText("") draw nothing. This is a common real-world input — any UI code rendering a text field/label whose content happens to be empty hits it.
Steps to reproduce
const { createCanvas } = require("canvas");
const ctx = createCanvas(10, 10).getContext("2d");
ctx.measureText(""); // process dies here, exit code 133 (SIGTRAP)
Same result with ctx.fillText("", 5, 5) and ctx.strokeText("", 5, 5). Non-empty strings (including " ", "\n", emoji, accented chars) all work fine. No font registration is required to reproduce.
Native backtrace
The trap is a brk instruction inside SheenBidi's paragraph creation — presumably an assertion on a zero-length paragraph:
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x1176079f4)
frame #0: canvas.node`SBAlgorithmCreateParagraph + 1512
canvas.node`SBAlgorithmCreateParagraph:
-> 0x1176079f4 <+1512>: brk #0x5516
Suggests the new text layout path calls into SheenBidi without first checking for zero-length input; per SheenBidi's API docs, SBAlgorithmCreateParagraph expects a paragraph length ≥ 1.
Environment
- canvas: 4.0.0-rc1 (prebuilt binary from npm, canvas-darwin-arm64)
- Node.js: v22.22.2
- OS: macOS 15.7.7 (Darwin 24.6.0), Apple Silicon (arm64)
Expected behavior
Match v3.x / browser behavior: measureText("") returns a TextMetrics with all-zero extents; fillText/strokeText with empty text are no-ops. An early return for zero-length input before entering bidi/layout would presumably fix all three entry points at once.
In canvas@4.0.0-rc1 ( PR #2602 ), passing an empty string to any of the text APIs, ctx.measureText(""), ctx.fillText("", x, y), or ctx.strokeText("", x, y), crashes the entire Node.js process with SIGTRAP (exit code 133). It's a native abort, not a JS exception, so it cannot be caught with try/catch and takes down the whole application.
In v3.x (and in browsers), these calls are valid no-ops: measureText("") returns metrics with width: 0, and fillText("")/strokeText("") draw nothing. This is a common real-world input — any UI code rendering a text field/label whose content happens to be empty hits it.
Steps to reproduce
Same result with ctx.fillText("", 5, 5) and ctx.strokeText("", 5, 5). Non-empty strings (including " ", "\n", emoji, accented chars) all work fine. No font registration is required to reproduce.
Native backtrace
The trap is a brk instruction inside SheenBidi's paragraph creation — presumably an assertion on a zero-length paragraph:
Suggests the new text layout path calls into SheenBidi without first checking for zero-length input; per SheenBidi's API docs, SBAlgorithmCreateParagraph expects a paragraph length ≥ 1.
Environment
Expected behavior
Match v3.x / browser behavior: measureText("") returns a TextMetrics with all-zero extents; fillText/strokeText with empty text are no-ops. An early return for zero-length input before entering bidi/layout would presumably fix all three entry points at once.