Description
Problem
Aborting from an ELK export callback can leave an underlying promise rejection unhandled even when the caller handles the rejection returned by layout().
This exists on master 0ff666b5d7dab77ccf3cb599b455387f1fc894ea and is carried unchanged by the master-to-dev synchronization in #3573. It should be addressed separately from that sync.
Reproduction
import { shapes } from '@joint/core';
import { layout } from '@joint/layout-elk';
const controller = new AbortController();
await layout([
new shapes.standard.Rectangle({ size: { width: 40, height: 30 } })
], {
signal: controller.signal,
exportElement() {
controller.abort(new Error('cancelled during export'));
}
}).catch(() => {});A local diagnostic using the actual built public layout entry point handled its returned rejection, then observed one separate unhandledRejection carrying the same abort reason. Depending on the runtime's unhandled-rejection policy, this can terminate a Node process; browsers can emit unhandledrejection.
Cause and expected behavior
layout() checks the signal before synchronous export callbacks run. An export callback can then abort it. layoutWithDefaultElk() creates a promise, but abortable() returns from its already-aborted branch before attaching handlers to that underlying promise. The underlying callback subsequently throws the abort reason.
The caller should receive the abort rejection without an additional unhandled rejection. Follow-up regression coverage should abort inside an export callback and verify that catching layout() consumes the failure fully, alongside normal and already-aborted cancellation paths.
Source: abort helper, layout entry point.
Verified report: #3573 (comment)
Activity
samuelgja commentedon Oct 29, 2026
Know. Issue, already tracked