Bug: Scheduled/Web trigger function invocation fails with wrapper-level TypeError, regardless of module type or function name
Summary
A named exported function in index.js, referenced correctly by manifest.yml’s handler field, cannot be invoked by the Forge runtime. Every invocation — whether via scheduledTrigger, webtrigger (manually curled), or after renaming the function to match the documentation’s own example — fails identically with:
TypeError: t[w.functionName] is not a function
at /mnt/wrapper/wrapper_v24_XX.cjs:2:905258
at g (/mnt/wrapper/wrapper_v24_XX.cjs:2:905264)
at new Promise (<anonymous>)
at /mnt/wrapper/wrapper_v24_XX.cjs:2:904992
at AsyncLocalStorage.run (node:internal/async_local_storage/async_context_frame:63:14)
at BufferedInvokeProcessor.Q [as handler] (/mnt/wrapper/wrapper_v24_XX.cjs:2:904238)
at BufferedInvokeProcessor.processInvoke (file:///var/runtime/index.mjs:1099:33)
at file:///var/runtime/index.mjs:1185:98
at InvokeStoreSingle.run (file:///var/runtime/index.mjs:83:12)
at _Runtime.runWithInvokeContext (file:///var/runtime/index.mjs:1198:24)
Note the wrapper filename version changes between invocations (wrapper_v24_5.cjs, wrapper_v24_20.cjs, wrapper_v24_25.cjs, wrapper_v24_35.cjs, wrapper_v24_50.cjs, wrapper_v24_85.cjs, wrapper_v24_100.cjs — all observed across different invocations of the same function).
A different exported function (index.handler, built via resolver.getDefinitions()) in the same file invokes correctly on every call — the app’s primary resolver-based UI functionality (search, export, saved filters, etc.) all work without issue.
Environment
- App ID:
ari:cloud:ecosystem::app/0628f03f-e8d8-4cdc-b5cc-9dedbe26ebd3 - Environment: development
- Site:
power-exporter-test.atlassian.net(also reproduced onarzenicstech.atlassian.net) - Runtime:
nodejs24.x,memoryMB: 512,architecture: arm64 - Forge CLI: updated to latest (13.1.0) partway through investigation — issue predates and persists after this update
Manifest (relevant excerpt, current state after testing)
modules:
jira:globalPage:
- key: power-exporter-page
resource: main
resolver:
function: resolver
title: Power Exporter for Jira
scheduledTrigger:
- key: schedule-runner
function: schedule-function
interval: hour
webtrigger:
- key: schedule-debug-trigger
function: schedule-function-debug
function:
- key: resolver
handler: index.handler
- key: schedule-function
handler: index.trigger
timeoutSeconds: 300
- key: schedule-function-debug
handler: index.trigger
Function (current, after multiple isolation attempts)
export const trigger = async () => {
try {
const prevHeartbeat = await storage.get('scheduler-heartbeat');
await storage.set('scheduler-heartbeat', {
lastInvokedAt: new Date().toISOString(),
invocationCount: ((prevHeartbeat && prevHeartbeat.invocationCount) || 0) + 1,
});
} catch (e) {
console.error('scheduleHandler: heartbeat write failed', e.message);
}
// ... (schedule-processing logic omitted for brevity, not reached — crash occurs before any app code runs)
return { statusCode: 204 };
};
Steps to reproduce
- Deploy an app with a
scheduledTriggermodule pointing to a named function export. - Wait for the hourly interval, or add a temporary
webtriggerpointing to the same handler and curl it directly. - Both invocation paths produce the identical
TypeError: t[w.functionName] is not a functionat the wrapper level — the function body never executes (confirmed via a storage-write placed as the literal first line of the function, which never persists).
What we’ve ruled out (each tested independently, with evidence)
| Hypothesis | Test performed | Result |
|---|---|---|
forge install --upgrade never run |
Ran it, confirmed via forge install list showing “Up-to-date” |
Ruled out — still crashes |
| Stale deployment (old bundle without this function) | Fresh forge deploy (full verbose log showing “Upsert scheduled triggers → DONE”), re-tested after |
Ruled out — crashes on freshly deployed code |
| Manifest handler string doesn’t match export name | Direct line-by-line comparison, index.scheduleHandler ↔ export const scheduleHandler |
Ruled out — exact match confirmed |
| Function-declaration vs arrow-function-const export style | Compared against a sibling app (same Atlassian account) using export const automationHandler = async () => {...}, which works correctly; switched to match exactly |
Ruled out — no change in behavior |
Missing required response shape ({statusCode}) |
Added return { statusCode: 204 } per scheduled trigger events docs |
Ruled out — crash occurs before this line is ever reached |
scheduledTrigger module itself is the faulty invocation path |
Added a temporary webtrigger pointing to the exact same function, invoked directly via curl |
Ruled out — identical crash via a completely different invocation mechanism |
| Bundler/tree-shaking stripping an export with no in-file callers | Added an explicit resolver.define() call referencing the function, forcing an in-file usage |
Ruled out — identical crash persists |
Function name itself (scheduleHandler) causing some obscure collision |
Renamed to trigger, matching the documentation’s own example exactly, in both manifest and export |
Ruled out — identical crash persists |
What still works, for contrast
The resolver.getDefinitions()-based handler export in the same file, wired via jira:globalPage’s resolver, works on every invocation — all app UI functionality (search, export, save filter, etc.) functions correctly. This isolates the issue specifically to functions invoked via scheduledTrigger/webtrigger handler strings pointing directly at a named export, as opposed to functions invoked via the resolver bridge.
Question for the Forge team
Given the above, this appears to be either:
- A build/bundling issue specific to how
scheduledTrigger/webtriggerhandler functions are packaged for this app (vs. resolver-based functions), possibly related to thenodejs24.xruntime or the wrapper version itself (note the wrapper file changes version on almost every invocation —wrapper_v24_5throughwrapper_v24_100observed), or - Some other platform-side issue with how this specific app’s
functionmodule definitions are being resolved at invocation time.
Could someone from the Forge platform team help identify what t and w.functionName refer to in the wrapper’s invocation code, so we can determine why the named export isn’t being found at runtime despite being correctly declared in the manifest and correctly exported in the source?
Happy to provide the full index.js, manifest.yml, or any additional logs needed.