Notes from the work

One email when a tutorial or migration write-up goes live. Nothing else, and an unsubscribe link in every email.

LeanZero

Two people in Romania doing Atlassian migrations, Forge apps and practical AI work for teams that would rather talk to the person doing the job. Most of what we learn ends up on this site.

Services

  • Atlassian Migrations
  • Atlassian FastShift
  • Atlassian Maintenance
  • Forge App Development
  • AI Development Consultation

Topics

  • Jira
  • Jira Service Management
  • Confluence
  • Bitbucket
  • Atlassian Forge
  • Cloud Migration
  • Local AI
  • AI Coding
  • Certifications
  • Atlassian Team
  • All topics

Company

  • Blog
  • Tutorials
  • Contact

Community

  • Join Discord
  • Support this site

© 2026 LeanZero. All rights reserved.

Privacy PolicyTerms of Service24/7 SupportTrust CenterSecurityDPALegal notice
  1. Home
  2. Blog
  3. Forge Bridge Unable To Emit Ready Event
"Uncaught (in promise) Error: Unable to emit ready event." in a Forge Confluence macro
Atlassian ForgeConfluence

"Uncaught (in promise) Error: Unable to emit ready event." in a Forge Confluence macro

Gabriela Perdum
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
1const emitReadyEvent = async () => {
2    const context = await view_1.view.getContext();
3    await events_1.events.emit(EXTENSION_READY, {
4        localId: context.localId
5    });
6    try {
7        const success = await callBridge('emitReadyEvent');
8        if (success === false) {
9            throw new errors_1.BridgeAPIError('Unable to emit ready event.');
10        }
11    }
12    catch (err) {
13        throw new errors_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
1const emitReadyEvent = async () => {
2    // Confluence export relies on events instead of `callBridge`
3    const context = await view_1.view.getContext();
4    // dispatches event 'forge.bridge.EXTENSION_READY' on product window
5    await events_1.events.emit(EXTENSION_READY, {
6        localId: context.localId
7    });
8    // TODO: Consider using the above event for static macros as well for to avoid two calls
9    // `callBridge` is used for static macros in XEP to signal that the forgeDoc is ready to be snapshotted
10    try {
11        await callBridge('emitReadyEvent');
12    }
13    catch {
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.

ShareXLinkedIn
PreviousLeanZero at Atlassian Team '26 Europe Amsterdam: no booth, here to talk Forge migrationsNextv0.5 of our Atlassian Qwen3.8-27B: beats v0.4, now with GGUF for llama.cpp and Ollama

Tags

#forge#confluence#forge-bridge#macro#pdf-export#error

Comments

0

Add a comment

Confirm your address using the email we send. You can unsubscribe at any time. Your email stays private and this does not sign you up for the newsletter.

Enjoyed?

Join our Discord community to discuss this article and get help with your own migration projects.

Discuss in Discord

On this page

  • Where @forge/bridge throws "Unable to emit ready event"
  • Why it was harmless
  • Which @forge/bridge version stopped it: 5.13.0
  • What to do
  • The part that is not in the docs

Join the Community

Join the community to ask questions, share your work, and connect with other professionals.

Join Discord

Found My Advice Helpful?

If I helped you solve something for free, coffee money is always appreciated (but never expected).

Buy Me a Coffee

Related reading

Is 25 Rovo credits per user enough? Cost and usage per action, measured

Rovo credits per user are 25, 70 or 150 a month on Jira and Confluence. We ran Rovo Chat on our own test org and read the meter after each message: Quick answers cost exactly 10, Think deeper cost 65 and 86. Then we worked out what that means for 50, 200 and 500 seats once billing starts on 3 December.

10/15/2026

DeepSeek V4.1 Flash coding benchmark: 0.33 on a Forge app upgrade, Opus 5.5 got 0.67

I ran DeepSeek V4.1 Flash twice through Forge 2.0, our Atlassian Forge app-upgrade benchmark. It scored 0.2893 and 0.3790, a median of 0.3342 against 0.6745 for Claude Opus 5.5 and 0.5283 for MiMo V2.6 Flash. Then I read the code behind its biggest penalty, and I don't think our scorer got that one right.

10/13/2026

Jira Gantt Chart Critical Path: Show the Work That Sets the Finish of a 1,992-Row Plan

Jira's timeline draws dependencies but no critical path. This walks a 1,992-row plan through the Timeline of LeanZero Management 6.4.0: the 28 work items that set the finish in their own lane, the plan folded to 40 rows, a card for each bar and a list of what needs a decision. All six steps were re-run on 12 October 2026 on the current version.

10/13/2026