"Uncaught (in promise) Error: Unable to emit ready event." in a Forge Confluence macro
Gabriela Perdum
Author
7 min readSeptember 18, 2026
Key takeaways
The error is from view.emitReadyEvent() in @forge/bridge 5.10.2 through 5.12.0: the library re-threw any failure of a bridge call that the Confluence host does not support in normal page view.
It was harmless. The EXTENSION_READY event that PDF export consumes is emitted before that bridge call in every version of the library, so the export signal had already gone out when the error fired.
5.13.0 (17 February 2026) removed the throw; the current 7.0.0 carries the same silent catch and a comment naming view mode as the reason. Upgrade @forge/bridge; no manifest or app-code change is needed.
Neither Atlassian docs page mentions the rejection. The only written explanation is a comment in the shipped source, which is where the answer to a library error usually is.
You have a Forge Custom UI macro in Confluence. To make PDF export faster and more reliable, you set emitsReadyEvent: true on the macro module and call await view.emitReadyEvent() once your data has loaded, exactly as the docs describe. From then on, every normal page view logs this in the browser console:
text
1Uncaught (in promise) Error: Unable to emit ready event.
The developer who put this to the Forge community on 2 February 2026 had done exactly that, to improve PDF export performance, and asked whether a rejection in normal page view was expected, because the docs do not say. The answer is in the shipped source of @forge/bridge.
Where @forge/bridge throws "Unable to emit ready event"
Unpack @forge/bridge@5.12.0 from the registry and open out/view/emitReadyEvent.js. The whole function is short:
js
1constemitReadyEvent=async()=>{2const context =await view_1.view.getContext();3await events_1.events.emit(EXTENSION_READY,{4localId: context.localId5});6try{7const success =awaitcallBridge('emitReadyEvent');8if(success ===false){9thrownewerrors_1.BridgeAPIError('Unable to emit ready event.');10}11}12catch(err){13thrownewerrors_1.BridgeAPIError('Unable to emit ready event.');14}15};
Three things happen in order. The function reads the macro's context. It emits an EXTENSION_READY event through the Forge events API, carrying the macro's local id. And then it makes a second call, callBridge('emitReadyEvent'), and if that call rejects, or resolves to false, it throws a BridgeAPIError with the message you are seeing. The class is a plain extends Error with no name override, which is why the console line says Error: rather than BridgeAPIError:.
The message is thrown from the library's own catch block. Whatever the Confluence host did with that second call in normal page view, the library's reaction was to convert it into an uncaught rejection in your macro.
Why it was harmless
The ordering is the whole story. The EXTENSION_READY event is what Confluence export consumes; Atlassian's own reference for emitReadyEvent says the function "leverages the Forge Events API to emit an EXTENSION_READY event" so that consumers "such as our PDF export service" can detect when a macro is fully loaded. That emit is the second line of the function, and it is outside the try block. By the time the bridge call fails, the export signal has already gone out.
The later source says so in as many words. In 6.2.0 the same file reads:
js
1constemitReadyEvent=async()=>{2// Confluence export relies on events instead of `callBridge`3const context =await view_1.view.getContext();4// dispatches event 'forge.bridge.EXTENSION_READY' on product window5await events_1.events.emit(EXTENSION_READY,{6localId: context.localId7});8// TODO: Consider using the above event for static macros as well for to avoid two calls9// `callBridge` is used for static macros in XEP to signal that the forgeDoc is ready to be snapshotted10try{11awaitcallBridge('emitReadyEvent');12}13catch{14// Silently ignore the error as this app may be calling this method in Confluence view mode where the method is not supported.15}16};
So the second call exists for a different consumer, static macros being snapshotted, and the host does not support it in Confluence view mode. Your Custom UI macro on a normal page was calling a method the library's own comment says is not supported in view mode, and the older library treated that as an error worth throwing. The Atlassian staff reply on the community thread, on 11 February, said the same thing without the mechanism: you can safely ignore it, it should have no impact on your app, and a fix is coming.
Which @forge/bridge version stopped it: 5.13.0
The fix landed in @forge/bridge@5.13.0, published on 17 February 2026, six days after that reply. The changelog entry is one line, "Remove error message from emitReadyEvent", and the code change is exactly that: the success === false check is gone, the catch block is empty, and the error import is removed. The error string appears in one file in 5.12.0 and in no file in 5.13.0, 6.2.0, 6.3.1 or the current latest, 7.0.0.
The current 7.0.0, published 14 September 2026, is byte-identical to 6.2.0 and 6.3.1 in this file, comments included; the 7.0.0 changes are elsewhere (fetch, the object-store types, the currentVersion removal) and its changelog does not mention emitReadyEvent. So the situation today is stable: the call is still made, the host still may not support it in view mode, and the library swallows the result.
Going the other way, the changelog says the bridge call was added in 5.10.2, on 5 January 2026, "for usage in static macros". That is the version where the error would first have appeared for a macro on a normal page; checked at source, 5.10.1 makes no bridge call at all and 5.10.2 already has the throw, so the window is 5.10.2 through 5.12.0 and that boundary is verified. The developer who reported it did so a day before 5.12.0 was published, so they were on one of those earlier releases.
What to do
Upgrade @forge/bridge to 5.13.0 or later. Nothing else changes: keep emitsReadyEvent: true in the manifest, keep the await view.emitReadyEvent() call after your data loads. The manifest property was never the problem. Its documentation says that it defaults to false and that it tells Confluence the macro "will send a emitReadyEvent when it has completed loading and is ready for export or further processing", and that is still what it does.
If you cannot upgrade yet, wrapping the call in your own try/catch is safe for the reason above: the export event has already been emitted before anything that can throw.
The part that is not in the docs
Neither the view bridge reference nor the macro module page says that emitReadyEvent rejects, throws, or is unsupported in view mode. Searching both pages for those words finds nothing about this function. The only written statement of what actually happens is the comment in the shipped source, added in 5.16.0. That is not unusual for Forge library errors, and it is the reason this site keeps reading the tarball rather than the changelog when a bridge call misbehaves: the package on the registry is the primary source, and it is one npm pack away.
Two things remain unknown from the outside. What the Confluence host returns for that bridge call in page view, a rejection or a false, cannot be observed from the package, only the library's reaction to it. The explanatory comment first ships in 5.16.0, on 27 April 2026, and that release's changelog entry does not mention it. Neither changes the answer: upgrade, and the console goes quiet.
How to fine-tune Qwen3.8-27B with LoRA on a Mac: MLX fine-tuning from scratch
Qwen3.8-27B and Qwen3.5-9B were taught Forge, Jira and Confluence on a single Mac Studio with 96 GB. This is the whole procedure with the seven scripts that do it, printed in full: a pinned MLX environment with two patches, an 8-bit base with its speculative-decoding head kept as a sidecar, a chat-JSONL dataset built from a folder of Markdown, a segmented trainer with full-state checkpoints, an adapter expander, a per-module merge into the 8-bit shards. The kit was run end to end for this page on the 9B, in 21 minutes, and its real output follows each step, including the one where the validation loss turns the wrong way.