Troubleshooting
This document catalogs known Forge platform bugs, common deployment failures, and their solutions. Each issue follows the format: Problem -> Cause -> Solution.
Known Forge Platform Bugs
Section titled “Known Forge Platform Bugs”These are open bugs in the Atlassian ECO Jira project that affect Foundation. A full audit of 80+ bugs is available at docs/plans/2026-03-17-forge-platform-bugs-audit.md.
CRITICAL
Section titled “CRITICAL”ECO-1310 / ECO-1346: CaaS Manifest Byte Size Limit
Section titled “ECO-1310 / ECO-1346: CaaS Manifest Byte Size Limit”Problem: Forge deploy fails during “Release CaaS manifest” with INTERNAL_SERVER_ERROR 500 when the manifest exceeds the CaaS byte limit.
Cause: The Forge CaaS manifest conversion has a hard limit of ~257KB after internal conversion. The CaaS conversion inflates raw manifest bytes by 8-10x, giving a practical raw byte ceiling of ~23,500 bytes.
Solution:
- Use the slim manifest format: descriptions=
., titles=x, 2-char function keys, no optional fields. - Run
cd foundation && npm run check:manifestbefore adding any new action. - Current state: 35 actions, ~22,850 bytes, ~650 bytes of headroom.
- If more actions are needed, consolidate via dispatcher functions (route multiple logical actions through one manifest entry with an
_actionparameter).
ECO-1090: Forge SQL Returns 5xx Instead of 429 for Rate Limits
Section titled “ECO-1090: Forge SQL Returns 5xx Instead of 429 for Rate Limits”Problem: SQL operations fail with 5xx errors that look like server failures but are actually rate limit violations.
Cause: When the Forge SQL rate limit (150 DML/second) is exceeded, the platform returns 5xx instead of 429. Application error handling cannot distinguish rate limits from real failures.
Solution:
- Currently no client-side workaround. Treat all 5xx from SQL operations as potentially rate-limit related.
- Implement exponential backoff on SQL 5xx errors.
- Atlassian has a fix in progress to return proper 429 responses. Once shipped, update error handling in
cache-manager.tsand all SQL-touching services.
ECO-395: Missing Events for Custom Workflow Notifications
Section titled “ECO-395: Missing Events for Custom Workflow Notifications”Problem: The issue cache goes stale silently. Users see outdated status, assignee, or priority data for up to 60 minutes.
Cause: avi:jira:updated:issue does NOT fire when a Jira workflow transition uses Custom Event Notifications. The event-driven cache sync has no way to detect these updates.
Solution:
- No workaround exists. This is a gap in the cache freshness model.
- The hourly cache integrity scheduled trigger (
cic) is the safety net, limiting the stale window to ~60 minutes. - Document this as a known limitation for customers using custom workflow events.
ECO-1356 / ECO-1198: Stale Context After SPA Navigation
Section titled “ECO-1356 / ECO-1198: Stale Context After SPA Navigation”Problem: The issue context panel shows data for the wrong issue after navigating between issues without a full page reload.
Cause: view.getContext() and resolver context return the previous page’s extension data after SPA navigation. This affects both useProductContext() and direct view.getContext() calls.
Solution:
- Verify
context.extension.issue.keymatches the expected issue before rendering. - Refreshing the page reloads the context correctly.
- Add a context-key guard in the issue context panel component.
ECO-277: Resolver undefined Becomes {} on Frontend
Section titled “ECO-277: Resolver undefined Becomes {} on Frontend”Problem: Frontend if (!data) checks fail to detect “no data” conditions. Components render when they should show empty states.
Cause: Any resolver that returns undefined (e.g., entity not found, empty result) is received by the frontend as {} (truthy). The Forge bridge coerces undefined to an empty object.
Solution:
- Never return bare
undefinedfrom resolvers. - Always return explicit
null,false, or{ data: null }. - Audit all resolvers for bare
undefinedreturns.
ECO-1297: False Re-Consent Prompt on 401 from Jira APIs
Section titled “ECO-1297: False Re-Consent Prompt on 401 from Jira APIs”Problem: Users see a confusing consent/re-authorization dialog when the actual issue is a permission error.
Cause: When asUser() Jira API calls return 401 (permission denied, expired session), Forge shows a consent dialog instead of passing the error through.
Solution:
- Wrap all
api.asUser()calls in try/catch. - Return controlled error objects instead of propagating raw API results.
- Foundation already does this in most resolvers.
ECO-1222: ADMINISTER Permission Not Recognized
Section titled “ECO-1222: ADMINISTER Permission Not Recognized”Problem: overrideEditableFlag and overrideScreenSecurity fail when using asUser() even with ADMINISTER permission.
Cause: These Jira API features only work with asApp(), not asUser(). This is a platform-level limitation.
Solution:
- Use
asApp()with explicit permission validation in app code for admin-level operations.
ECO-1307: Reserved Key “schema” in Resolver Payloads
Section titled “ECO-1307: Reserved Key “schema” in Resolver Payloads”Problem: Resolver invocations fail with a schema validation error.
Cause: The Forge bridge validates payloads and rejects invocations that contain "schema" as a top-level key.
Solution:
- Never use
"schema"as a key in resolver payloads. - Rename any payload keys that might collide.
ECO-508: Missing changelog in Issue Updated Events
Section titled “ECO-508: Missing changelog in Issue Updated Events”Problem: Issue update events sometimes lack the changelog field, forcing a full issue re-fetch (2 rate limit points per event).
Cause: When generateChangelog=false (triggered by certain API calls), the changelog is omitted from the event payload.
Solution:
- Always fall back to full issue re-fetch when
changelogis absent. - Budget for extra API calls in rate limit calculations.
MEDIUM
Section titled “MEDIUM”| Bug | Problem | Solution |
|---|---|---|
| ECO-1321 | postrobot_method console error when navigating away from Foundation | Cosmetic only; no fix needed |
| ECO-1355 | Admin page not clickable with custom domains | Use standard .atlassian.net domain |
| ECO-1246 | Missing entity properties in trigger event payloads | Make additional API calls per event |
| ECO-1174 | Chrome Local Network Access blocks Custom UI fetch | Users disable Chrome flag or use Firefox |
| ECO-303 | Icons not loaded in Custom UI on Firefox | Users disable Enhanced Tracking Protection |
| ECO-226 | jira:globalPage broken for JSM “customer” users with Jira license | No workaround |
| ECO-1280 | --ds-radius-*, --ds-font-* tokens not set by view.theme.enable() | Use hardcoded fallbacks in CSS |
| ECO-1309 | Project icon avatar returns 403 | Fall back to default project icons |
Common Deployment Failures
Section titled “Common Deployment Failures”Build Fails: npm run build Errors
Section titled “Build Fails: npm run build Errors”Problem: Frontend build fails with compilation errors.
Solution:
- Check for TypeScript errors:
cd foundation && npx tsc --noEmit - Check for missing dependencies:
cd foundation/static/main && npm install - Verify
.npmrcexists in all four packages withlegacy-peer-deps=true
Deploy Fails: CaaS Manifest Release 500
Section titled “Deploy Fails: CaaS Manifest Release 500”Problem: forge deploy fails at “Release CaaS manifest” with 500 error.
Cause: Either the manifest exceeds the byte limit (ECO-1310) or the environment is corrupted.
Solution:
- Check manifest size:
cd foundation && npm run check:manifest - If manifest is within budget, the environment is likely corrupted.
- Test in a different environment:
forge deploy --environment staging - If another environment succeeds, create a fresh one:
Terminal window cd foundation && forge environments create --environment dev-new --non-interactivecd foundation && forge deploy --environment dev-newcd foundation && forge settings set default-environment dev-new
Deploy Fails: “Not a Forge project”
Section titled “Deploy Fails: “Not a Forge project””Problem: Forge CLI cannot find manifest.yml.
Cause: Running from the wrong directory.
Solution: Always run Forge commands from the foundation/ directory, not the repository root.
Deploy Blocked: Worktree Detection
Section titled “Deploy Blocked: Worktree Detection”Problem: Deploy script exits with “Deploy blocked: running from a git worktree.”
Cause: The deploy.sh script detects it’s running from a git worktree.
Solution: Merge your worktree branch back to main, then deploy from the main working tree.
Install Fails: Fresh Installation
Section titled “Install Fails: Fresh Installation”Problem: forge install fails on a Jira site.
Cause: The manifest has too many actions/modules, or there’s a conflict with a previous installation.
Solution:
- Check if the old installation is still present:
forge install list - Uninstall the old one from Jira admin panel if needed (destructive — get user approval)
- Verify the manifest action count is within limits
- Retry installation
node_modules Issues
Section titled “node_modules Issues”Stale Symlinks in Worktrees
Section titled “Stale Symlinks in Worktrees”Problem: Build or test commands fail with “Cannot find module” errors in a worktree.
Cause: Worktree node_modules symlinks point to the wrong path (project was moved or cloned elsewhere).
Solution:
- The
setup-worktree.shhook should auto-detect and fix stale symlinks before every Bash command. - If it doesn’t resolve:
Terminal window # Remove the stale symlink manuallyrm foundation/node_modulesrm foundation/static/main/node_modules# Re-run any command to trigger the hook, or run the script directlybash .claude/hooks/setup-worktree.sh
.gitignore Tracking Symlinks
Section titled “.gitignore Tracking Symlinks”Problem: git status shows node_modules as a tracked file.
Cause: .gitignore has node_modules/ (with trailing slash) which matches directories but NOT symlinks. Git treats symlinks as files.
Solution:
.gitignoremust usenode_modules(no trailing slash) to match both directories and symlinks.- Remove any tracked symlinks:
git rm --cached foundation/node_modules(and similar for sub-packages).
Main Tree node_modules Is a Symlink
Section titled “Main Tree node_modules Is a Symlink”Problem: Worktree hooks fail because the main tree node_modules is a symlink instead of a real directory.
Cause: An incorrect setup or a stale worktree symlink was created in the main tree.
Solution:
- The main tree’s
node_modulesmust always be a real directory fromnpm install. - Remove the symlink and reinstall:
Terminal window rm foundation/node_modulescd foundation && npm install
AG Grid Issues in Forge Iframe
Section titled “AG Grid Issues in Forge Iframe”Phantom Row Hover Highlights
Section titled “Phantom Row Hover Highlights”Problem: Hover highlights appear below the last data row in empty grid space.
Cause: AG Grid has a JS-based hover system (.ag-row-hover class) that calculates hover position from Y coordinates. It fires even in empty viewport space because AG Grid computes a virtual row index regardless of whether a row exists.
Solution:
suppressRowHoverHighlight={true}on<AgGridReact>disables the JS hover system.- Set
--ag-row-hover-color: transparentto prevent residual AG Grid hover styling. - Implement hover via CSS
.ag-row:hoverinfoundation.css(only fires on actual DOM elements). - Never re-enable AG Grid’s JS hover or set
--ag-row-hover-colorto a non-transparent value.
Inline Editor Destroyed on State Update
Section titled “Inline Editor Destroyed on State Update”Problem: Cell editors close immediately after opening, or flicker and disappear.
Cause: stopEditingWhenCellsLoseFocus={true} is incompatible with Forge iframe — focus tracking breaks at the cross-origin boundary. Any useState setter in AG Grid editing callbacks causes a re-render that destroys the active editor.
Solution:
- Set
stopEditingWhenCellsLoseFocus={false} - Use
useRefinstead ofuseStatefor theisEditingflag - Zero state updates in AG Grid editing callbacks
- Manual mousedown click-outside handler using a
gridDivRefon the wrapper div
Popup Editors Invisible or Mispositioned
Section titled “Popup Editors Invisible or Mispositioned”Problem: Dropdown editors (status, priority, assignee) don’t appear or appear at the wrong Y position.
Cause: AG Grid’s .ag-root-wrapper has overflow: hidden. When cellEditorPopup: true, the popup renders inside the wrapper and gets clipped. Additionally, AG Grid’s popup positioning is broken inside Forge iframes.
Solution:
- Remove
cellEditorPopup: truefrom status, priority, and assignee editors — render them inline instead. - For editors with custom DOM dropdowns (like AssigneePicker), use
position: fixedon the dropdown and calculate coordinates fromgetBoundingClientRect(). - Never use
cellEditorPopup: truefor editors with native<select>elements in a Forge iframe.
Click-Outside Handler Broken in AG Grid v35
Section titled “Click-Outside Handler Broken in AG Grid v35”Problem: Click-outside detection for inline editors doesn’t work.
Cause: AG Grid v35 removed the eGridDiv property from AgGridReact. Code using gridRef.current?.eGridDiv gets undefined, making the click-outside handler a no-op.
Solution:
- Use a
gridDivRefon the wrapper div element instead ofeGridDiv. - Attach
mousedownlisteners todocument, checking if the event target is inside the grid wrapper.
Forge SQL Rate Limit Confusion
Section titled “Forge SQL Rate Limit Confusion”Problem: SQL operations fail intermittently with 5xx errors that are difficult to diagnose.
Cause: Forge SQL rate limit violations (150 DML/second exceeded) return 5xx instead of 429 (ECO-1090). This makes rate limits indistinguishable from actual server failures.
Solution:
- Implement exponential backoff on all SQL 5xx errors.
- Monitor operation frequency and batch where possible.
- The fix from Atlassian (proper 429 responses) is in progress. Once available, update error handling in
cache-manager.tsand all SQL-touching services to handle 429 with appropriate backoff.
Debugging in Forge Custom UI
Section titled “Debugging in Forge Custom UI”Console.log Is Invisible
Section titled “Console.log Is Invisible”Problem: console.log statements in the frontend produce no visible output.
Cause: Forge iframe console messages don’t cross to the parent page. window.parent.postMessage is blocked by CSP.
Solution:
- Use visible DOM elements (debug banners) rather than console.log.
- Use large fonts (13px+),
pre-wrapwhite-space, andword-break: break-all. - Remove debug elements before committing.
Screenshots Can See Inside the Iframe
Section titled “Screenshots Can See Inside the Iframe”Browser-level screenshots CAN capture the content inside the Forge Custom UI iframe. Use screenshot tools to verify visual rendering after deployment.
Interactive Testing Limitations
Section titled “Interactive Testing Limitations”Automated click interactions cannot reliably reach inside the Forge iframe. Interactive testing (clicking buttons, opening menus, filling forms) must be done manually by the user.