Subject: Critical: invoke
call fails with “out is not a constructor” in minimal Forge Custom UI app (both tunnel & deploy)
Description:
We are consistently encountering a critical error when attempting to use the invoke
method from @forge/bridge
within a minimal Create React App (CRA) based Custom UI app. The error Error: There was an error invoking the function - out is not a constructor
originates deep within the @forge/bridge
and post-robot.js
internals, indicating a fundamental issue with the communication channel between the Custom UI iframe and the Atlassian host product.
This issue occurs both when running forge tunnel
(local development server proxied) and after forge deploy
(production bundle on Atlassian’s infrastructure), indicating it is not related to production build optimizations or typical CSP differences between environments.
Steps to Reproduce (Minimal Reproduction):
-
Initialize a new Create React App project:
pnpx create-react-app minimal-forge-test --template typescript cd minimal-forge-test
-
Install
@forge/bridge
and set React to a stable 18.x version:pnpm add @forge/bridge
-
Address a common ESLint conflict (specific to CRA/pnpm):
pnpm add eslint-plugin-react@latest eslint-config-react-app@latest -D pnpm store prune
(Note: This step is needed to resolve a local build blocker, but the core issue persists after it.)
-
Modify
src/App.tsx
to include theinvoke
call:import React, {useEffect} from 'react'; import logo from './logo.svg'; import './App.css'; import {invoke} from '@forge/bridge'; // Ensure @forge/bridge is imported function App() { useEffect(() => { const callForgeInvoke = async () => { console.log("App Component: Attempting invoke..."); // This log IS seen in console try { const res = await invoke("test"); // This is the line that throws the error console.log("App Component: Invoke successful:", res); } catch (e: any) { console.error("App Component: Invoke failed:", e); // This catches the "out is not a constructor" } } callForgeInvoke(); }, []); // Empty dependency array to run once on mount return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Forge App loaded. Check console for invoke attempt and error. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } export default App;
-
Build the Custom UI project:
pnpm run build
(This should now complete successfully.)
-
Set up the Forge Manifest (in a separate Forge project directory, e.g.,
../your-forge-app-root/manifest.yml
):# manifest.yml modules: jira:issuePanel: - key: llm-issue-panel resource: issue-panel-ui title: Minimal Forge App resolver: function: main icon: resources/icon.svg # Or provide a dummy one function: - key: main handler: index.handler # This resolver must exist and return a simple object resources: - key: issue-panel-ui path: ../minimal-forge-test/build/ # Path to the build output of your minimal CRA app permissions: content: scripts: - 'unsafe-eval' # Added this to resolve NS_ERROR_CONTENT_BLOCKED, but issue persists styles: - 'unsafe-inline' app: runtime: name: nodejs20.x # Or nodejs22.x - specify your Node.js runtime
(Ensure
resources/icon.svg
exists or point to a valid resource) -
Create a minimal resolver function (e.g.,
src/index.ts
in your Forge project’sfunctions
directory):// src/index.ts import Resolver from '@forge/resolver'; const resolver = new Resolver(); resolver.define('test', async ({ payload, context }) => { console.log('Resolver received "test" call from UI.'); return { status: 'success', message: 'Hello from Forge resolver!' }; }); export const handler = resolver.get;
-
Deploy the Forge app:
fctl deploy
(This should eventually pass the linting stage, given
moduleResolution: "node"
is used in the maintsconfig.json
for the Forge app itself if applicable, or that forge lint behaves as expected) -
Run with
forge tunnel
(or view deployed app in Jira):forge tunnel
Navigate to the Jira issue panel where the app is embedded.
Expected Outcome:
The app should load, and the invoke("test")
call should successfully communicate with the resolver.
Actual Outcome:
The app loads, the App Component: Attempting invoke...
log appears, but then an Uncaught (in promise) Error: There was an error invoking the function - out is not a constructor
is thrown in the browser console.
Error Stack Trace (from browser console):
Error: There was an error invoking the function - out is not a constructor
invoke useBridge.tsx:317
o metrics.ts:81
f bridge-provider.js:61
eA post-robot.js:1727
try post-robot.js:621
eA post-robot.js:1724
eA post-robot.js:1807
t post-robot.js:2135
try post-robot.js:621
t post-robot.js:2128
i helpers.ts:100
d trycatch.ts:233
c dom.ts:107
eL post-robot.js:2119
X post-robot.js:906
getOrSet post-robot.js:993
eL post-robot.js:2117
e post-robot.js:2199
r post-robot.js:13
<anonymous> useBridge.tsx:37 (relevant snippet: `createIframeBridge = require('@atlassian/bridge-core').createIframeBridge;`)
n DP-35:74
c async-forge-ui-issue-view-extension.ec4712b2.js:14
React 8
error global-bridge.js:2
Se global-bridge.js:2
Se global-bridge.js:2
Troubleshooting Steps Already Performed:
- React Version: Downgraded from React 19 (beta) to stable
18.3.1
. @forge/bridge
Version: Using latest4.5.3
.- CSP: Added
script-src 'unsafe-eval'
tomanifest.yml
(resolved aNS_ERROR_CONTENT_BLOCKED
but not the primary error).style-src 'unsafe-inline'
is also present. - Build Environment: Issue occurs with both
forge tunnel
(local dev server) andforge deploy
(production build). craco.config.cjs
: For minimal test, no custom Craco config is used. For original app,webpack.resolve.fallback
andwebpack.plugins.ProvidePlugin
entries (Node.js polyfills) were removed/commented out and confirmed not to be the cause.- TypeScript
moduleResolution
: For minimal CRA, it uses defaultBundler
. For the containing Forge app, triednode
,classic
,node16
,nodenext
to appeasefctl deploy
linting, but the coreinvoke
error persists regardless. - Browser Isolation: Tested in Incognito mode and different browsers (Chrome, Firefox).
- Cache Clearing: Performed
rm -rf node_modules pnpm-lock.yaml
,pnpm store prune
, and browser cache clears (Clear site data
). - Delay before Invoke: Introduced
setTimeout
(up to 1.3 seconds) before the firstinvoke
call; the error still occurs.
Request:
This issue seems to be a fundamental problem with how @forge/bridge
or its underlying communication library (post-robot
) initializes or operates within the Forge Custom UI iframe, as it’s reproducible with a minimal setup and stable dependencies. We are unable to proceed with development due to this critical blocker.
Any insights or direct assistance would be greatly appreciated.