We have a Forge Custom UI macro that declares adfExport and returns null for exportType ‘pdf’. A page with 18 or more of these macros never exports in the new PDF export dialog. The dialog shows “Something went wrong” after about 4 to 4.5 minutes. The export status stays IN_PROGRESS at 0% the whole time.
The same page exports in about 60 s when the manifest does not declare adfExport. It exports in 31 s when the handler returns ADF for PDF instead of null.
Returning null for PDF is the documented way to get the native PDF render. That comes from Atlassian’s comment on CONFCLOUD-83083, which was closed as Fixed on 26 Jun 2026.
Environment
- Confluence Cloud, the new PDF export dialog (••• → Export → PDF, as a popup), on 24 Sep 2026 between about 08:50 and 16:30 UTC.
- Forge Custom UI macro, runtime nodejs22.x, @forge/bridge 7, emitsReadyEvent: true. We also tried emitsReadyEvent: false, and it made no difference.
- No permission scopes and no egress.
The app’s shape (manifest):
modules:
macro:
- key: diagram
resource: view
title: Diagram
emitsReadyEvent: true
adfExport:
function: adf-export
function:
- key: adf-export
handler: index.handler
resources:
- key: view
path: static/view/dist
app:
runtime:
name: nodejs22.x
The export handler:
export const handler = async (payload) => {
if (payload?.exportType === 'pdf') return null; // native PDF render
return {
type: 'doc',
version: 1,
content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Diagram' }] }],
};
};
Steps
- Deploy and install the app on a Cloud site.
- Create a page with N instances of the macro, each with ordinary content, and publish it.
- Choose ••• → Export → PDF in the new dialog.
Results (same app, same site, same day)
- 1 macro, adfExport declared, handler returns null for PDF: ready in under 30 s.
- 8 macros, adfExport declared, null for PDF: ready, but slowly.
- 18 macros, adfExport declared, null for PDF: “Something went wrong” after about 4 min, IN_PROGRESS 0% throughout.
- 30 macros, adfExport declared, null for PDF: “Something went wrong” after about 4.5 min, IN_PROGRESS 0% throughout.
- 30 macros, adfExport declared, emitsReadyEvent: false, null for PDF: same failure.
- 18 macros, adfExport not declared: ready in under 52 s, every macro rendered.
- 30 macros, adfExport not declared: ready in about 60 s, every macro rendered.
- 30 macros, adfExport declared, handler returns ADF for PDF (mediaSingle with an external data: PNG): ready in 31 s.
forge logs shows one invocation per macro. They all arrive within about a second, and each returns in under a second. So the handler is not slow and does not throw. The invocation counts are far below the documented invocation limits.
Expected
The page exports, and each macro that returned null is rendered natively, as it is when adfExport is not declared.
Questions
- When adfExport returns null for PDF, does the exporter render each of those macros separately (for example, one headless render per macro) instead of rendering the page once? That would explain why the time grows with the number of macros until the export fails.
- Is there a limit on how many null-fallback macros one PDF export can have? If so, please document it.
- Can this be fixed, so that null for PDF behaves like a macro that does not declare adfExport?
Related
- CONFCLOUD-83083 (Bug, Fixed 26 Jun 2026): return null for PDF to fall back to native PDF rendering.
- CONFCLOUD-82734 (Suggestion, Gathering Interest): pages with 100 or more Forge macros hit the invocation rate limit. This is probably a different problem: our failure starts at 18 macros, and every invocation succeeds quickly.
- Confluence PDF Export has changed again - and Forge PDF exports are failing (23 Sep 2026): Forge macros fail to export in the new PDF dialog. It may share a cause.
Hi @RolfEriksen,
The one call per instance you’re seeing is documented. The macro manifest reference says “The adfExport function is invoked once per macro instance during export operations” and warns about “rate limiting and performance issues”. Whether the exporter then renders each null fallback separately, neither the docs nor our logs show.
Tried to reproduce it on a test site today. UI Kit and Custom UI (emitsReadyEvent) macros returning null for pdf, at 1, 8, 18 and 30 instances, plus a real-ADF control. All exported in 20-37s.
Not a limit that shows up everywhere then. Could still be tenant or content dependent, Chris saw a 17-macro page fail on the 24th in 102857, and ours was trivial content on a different site a day later.
Does it still fail for you today, and which region is the site in?
Hi, thanks for trying to reproduce it.
Yes, it still fails for us today (25 Sep, 11:30 - 12:00~ GMT+2 ). Same site, same Custom UI macro (emitsReadyEvent on, adfExport returning null for pdf), pages with N copies of one small Mermaid sequence diagram:
1 macro: PDF ready in 31 s
8 macros: ready in 73 s
12 macros: “Something went wrong - An unexpected error occurred” after 245 s
18 macros: same error after 242 s
Region: data residency isn’t pinned for this site, so Atlassian chooses where it’s hosted and we can’t see which AWS region it’s in. The site and users are in Norway.
Two things that might explain why yours passed:
-
The failures come at an almost constant 4 minutes, while going from 1 to 8 macros only added about 6 s per macro. That looks more like something hanging until a timeout than an export that is just slow. It also fits Chris’s 17-macro failure in 102857.
-
Our macro isn’t trivial. Each iframe loads Mermaid (a roughly 635 kB core chunk plus diagram code) and renders an SVG before it calls emitReadyEvent. If the null fallback renders every instance in the export browser at once, heavier iframes may be what tips it over. A guess, can’t see the exporter side.
For comparison: on 24 Sep the same 30 diagrams exported in about 60s when the manifest didn’t declare adfExport at all. The slowdown only appears once adfExport is declared and returns null for pdf.
What we do now: the handler returns a stored PNG for pdf when the diagram has one (mediaSingle with an external data: URI), and a 30-diagram page exports in about 31 s that way. Only diagrams without a stored picture still take the null path.
Happy to share page IDs, forge logs with timestamps, or build a minimal macro that loads a large bundle before emitting ready, if that would help narrow it down.
You were right about the content. My “not a limit that shows up everywhere” was wrong, our first run was trivial content.
Reproduced. Heavy mermaid Custom UI macro returning null for pdf: N=1 35s, N=8 38s, N=12 failed at 241s twice, N=18 at 254s.
Real ADF instead, N=18 in 36s. No adfExport at all, 23s with all 18 diagrams rendered.
Heavy iframes plus the null fallback then, and with our bundle at 3.5 MB against your ~635 kB the cutoff for yours can land somewhere else, but the hard stop near 240s still fits your timeout read.
Any non-null ADF skips that path. Your PNG numbers line up. If adfExport only exists to return null for pdf, dropping it gave us the native render with no failure.
Filing it with Atlassian with these numbers and linking the ticket here would help the next person.
Thanks! Filed with Atlassian as ECOHELP-169006, including the timings, control tests and your independent reproduction from this thread.
Chris in 102857 has a case that does not fit the weight read. His macros are small and inline, the pages are not large, and the PDFs come out at 2 or 3 pages, yet he saw failures and timeouts climb once a page had 5 or more of them. If the stop is content weight, 5 small inline macros should be nowhere near it, so it looks like a second shape of the same bug and worth adding to ECOHELP-169006. Rolf, did your 8 macro run ever fail on a retry, or was it clean every time?
One thing in your two runs stands out. Your bundles differ by more than five times, 635 kB against 3.5 MB, but the cliff sits in the same place, last pass at 8 and first failure at 12. If the stop were about iframe weight, the heavier app should have hit it earlier. Nobody has tried 9, 10 or 11 yet, so the cliff is only bracketed. If it lands on the same N for both of you, that points at a fixed slot count in the exporter rather than content weight, which is a much narrower thing to hand Atlassian than what is in the ticket now.
Hi @VladymyrPostnov,
Ran 9, 10 and 11. Each failed twice. 8 failed twice then passed twice. 12 passed twice. The cutoff I read into 8 and 12 in #4 was wrong.
One session, 19 exports, same site:
heavy macro (3.5 MB mermaid), null for pdf
N=1 pass 20s
N=8 fail 240s, fail 245s, later pass 36s, pass 36s (same page, fresh page)
N=9 fail 242s, fail 468s
N=10 fail 240s, fail 240s
N=11 fail 240s, fail 242s
N=12 pass 38s, pass 318s
light macro (237 kB), same null handler
N=12 pass 49s, pass 35s
N=18 pass 39s
N=30 pass 59s
controls, heavy macro at N=10 (run last, after the final failure)
real ADF pass 35s
no adfExport pass 36s
No fixed slot count on this app. 240s isn’t a hard stop, one pass at 318s and one fail at 468s, and failures sat at 0% then flipped to FAILED at 99. Every failure had the heavy iframes with the null fallback and the light runs sat between heavy failures, but the controls show nothing this time because both ran in the last five minutes when heavy null at 8 also passed. All 8 failures fell between 21:50 and 22:37 UTC on the 11th.
Not tested: Rolf’s app or tenant, small inline macros like Chris’s. Cause of the intermittency unknown.
@RolfEriksen the #4 numbers went into ECOHELP-169006, so these probably belong there too.