Skip to content

fix(deployment): prevent trigger registry initialization crash - #6342

Merged
waleedlatif1 merged 3 commits into
stagingfrom
fix/deploy-trigger-registry-cycle
Aug 6, 2026
Merged

fix(deployment): prevent trigger registry initialization crash#6342
waleedlatif1 merged 3 commits into
stagingfrom
fix/deploy-trigger-registry-cycle

Conversation

@BillLeoutsakosvl346

Copy link
Copy Markdown
Contributor

Summary

Fixes workflow deployment requests that crash while initializing the trigger registry.

PR #6272 made the deploy path import the trigger barrel before the block registry. Trigger definitions reference block configs during module initialization, which allowed the trigger barrel to be re-entered before TRIGGER_REGISTRY was ready. This change explicitly initializes the block registry first and preserves the existing deployment behavior.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Testing

  • bun run test -- lib/webhooks/deploy.test.ts triggers/webhook-url.test.ts (29 passed)
  • bunx biome check apps/sim/lib/webhooks/deploy.ts
  • bun run check:api-validation
  • Clean Next.js compilation of POST /api/workflows/:id/deploy reaches authentication and returns the expected 401 without a session instead of the TRIGGER_REGISTRY 500

Reviewers should focus on the explicit registry initialization at the top of apps/sim/lib/webhooks/deploy.ts.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Relevant tests are passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

Not applicable; this is a server-side module-initialization fix with no UI changes.

@vercel

vercelBot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
docsReadyReadyPreviewAug 6, 2026 10:01pm

Request Review

@cursor

cursorBot commented Aug 6, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches core trigger/block registry initialization order and many trigger fetchOptions paths; behavior should match prior store reads but async dynamic imports add a new failure surface if stores load late.

Overview
Breaks the triggers ↔ blocks module initialization cycle that can throw ReferenceError: Cannot access 'TRIGGER_REGISTRY' before initialization when @/triggers is evaluated before @/blocks (e.g. deploy paths that no longer accidentally import blocks first).

Mock payloads move from trigger-utils into dependency-free mock-payload.ts; @/triggers/index imports that module instead of trigger-utils, so the trigger barrel no longer pulls in code that statically depends on @/blocks.

Editor sub-block reads in trigger definitions no longer statically import useSubBlockStore / workflow registry stores (which transitively reach @/blocks). New triggers/editor-state.ts exposes async helpers that dynamic import() the stores and workflow option fetchers at call time, used across Gmail/HubSpot/IMAP/Outlook/Webflow/ClickUp/table/sim workspace triggers.

CI enforcement: scripts/check-trigger-block-cycle.ts walks static import graphs from triggers/index.ts and triggers/registry.ts and fails if blocks/ is reachable; wired as bun run check:trigger-block-cycle in CI and root package.json.

Reviewed by Cursor Bugbot for commit c9aa3ec. Configure here.

@greptile-apps

greptile-appsBot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR removes the trigger registry’s static dependency path back through block-aware editor utilities, moving editor-state access behind call-time imports and adding a CI audit to prevent the initialization cycle from returning.

  • Extracts dependency-free mock-payload generation for use by the trigger registry.
  • Replaces trigger option resolvers’ static Zustand-store imports with asynchronous editor-state readers.
  • Adds a trigger-to-block static dependency graph check to package scripts and CI.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

FilenameOverview
apps/sim/triggers/editor-state.tsIntroduces call-time editor-state readers so trigger definition evaluation no longer statically loads block-dependent stores.
apps/sim/triggers/index.tsRedirects sample-payload generation to the new dependency-free module, removing the registry’s static path through trigger utilities.
apps/sim/lib/workflows/triggers/mock-payload.tsPreserves mock-payload generation behavior in a module that does not depend on blocks or trigger utilities.
scripts/check-trigger-block-cycle.tsAdds a static reachability audit that detects value-import paths from trigger registry entrypoints into block modules.
apps/sim/triggers/table/poller.tsMigrates table option resolution to asynchronous editor-state helpers while preserving its table lookup behavior.
.github/workflows/test-build.ymlRuns the new trigger/block initialization-cycle audit in CI.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
Deploy[Deployment import] --> Triggers[Trigger registry]
Triggers --> Mock[Dependency-free mock payload]
Triggers --> Definitions[Trigger definitions]
Definitions -. option resolver invocation .-> EditorState[Dynamic editor-state imports]
EditorState --> Stores[Workflow and sub-block stores]
Stores --> Blocks[Block registry]
Audit[CI cycle audit] --> Triggers
Audit --> Definitions
Loading

Reviews (2): Last reviewed commit: "Merge remote-tracking branch 'origin/sta..." | Re-trigger Greptile

Replaces the import-order guard from the previous commit with the structural fix.
Block configs spread `getTrigger('...').subBlocks` while their module body runs, so
`blocks/*` depends on `triggers/*` by design. Thirteen edges closed the loop back the
other way, which made module evaluation order load-bearing: enter the graph through
`@/triggers` and a block config calls `getTrigger()` before `TRIGGER_REGISTRY` is
initialized, throwing
ReferenceError: Cannot access 'TRIGGER_REGISTRY' before initialization
Eleven deployment routes crashed on import: `POST /api/workflows/[id]/deploy`, the v1
public and admin deploy/rollback/activate routes, both deployment-version routes, and
the three custom-tool deployment routes. All of them funnel through
`lib/webhooks/deploy.ts`, which stayed safe only because it imported a value from
`@/blocks` — biome sorts that above `@/triggers`, so the safe barrel always evaluated
first. #6272 deleted that import as unused cleanup and took the whole surface with it.
The reverse edges came from two places, both layering violations rather than anything
inherent to triggers:
- `triggers/index.ts` imported the mock-payload generator from `trigger-utils`, which
imports `@/blocks` for unrelated helpers. The generator is pure, so it moves to
`lib/workflows/triggers/mock-payload.ts` and both callers import it there.
- Eleven trigger modules statically imported the editor's Zustand stores to read
sub-block values inside `fetchOptions`/`fetchOptionById`. Those reads now go through
`triggers/editor-state.ts`, which loads the stores with a dynamic `import()` —
resolved when the resolver is called, not during module evaluation, so it carries no
initialization-order obligation.
Side effect: `@/triggers` drops from 744 statically reachable modules to 526. The block
registry, the workflow Zustand stores and their React Query graph are no longer pulled
into every server module that imports a trigger.
`scripts/check-trigger-block-cycle.ts` fails the build if a static edge returns, and
reports the shortest offending chain. The existing suite could not have caught this —
`deploy.test.ts` mocks both `@/blocks/registry` and `@/triggers`, and `vitest.setup.ts`
mocks `@/blocks/registry` globally, so it passed 18/18 against the broken code.
@waleedlatif1
waleedlatif1 requested a review from a team as a code ownerAugust 6, 2026 21:53
@waleedlatif1

Copy link
Copy Markdown
Collaborator

Investigated this in depth and pushed a follow-up commit that replaces the import-order guard with the structural fix. Summary of what I found, since some of it changes how the PR should be read.

The bug is real, and it is wider than one route

Reproduced deterministically — a single import is enough:

$ bun -e "import('@/triggers')"
ReferenceError: Cannot access 'TRIGGER_REGISTRY' before initialization
at getTrigger (apps/sim/triggers/index.ts:71:19)
at apps/sim/blocks/blocks/airtable.ts:294:8

I wrote an ESM evaluation-order simulator, validated it against runtime results, ran it over 2,961 modules, then empirically probed all 1,007 API routes under Bun. Both agree exactly: 11 routes, not one.

app/api/workflows/[id]/deploy <- editor "Deploy"
app/api/workflows/[id]/deployments/[version] (GET)
app/api/workflows/[id]/deployments/[version]/revert
app/api/v1/workflows/[id]/deploy
app/api/v1/workflows/[id]/rollback
app/api/v1/admin/workflows/[id]
app/api/v1/admin/workflows/[id]/deploy
app/api/v1/admin/workflows/[id]/versions/[versionId]/activate
app/api/tools/deployments/deploy | promote | undeploy

They all funnel through lib/webhooks/deploy.ts, so the original one-line guard did fix all 11.

Bisect confirms #6272

deploy.ts atresult
6cc25f6d9^ (pre-#6272)imports fine
6cc25f6d9 -> stagingReferenceError

#6272 removed import { getBlock } from '@/blocks' as unused cleanup. Biome sorts @/blocks/* above @/triggers/*, so that import was silently guaranteeing the safe barrel evaluated first. The underlying cycle is much older (triggers/index -> trigger-utils dates to #2160, 2025-12-02) — #6272 only removed the accident hiding it.

Production is not affected

Worth stating plainly before anyone treats this as a P0. Checked CloudWatch (/ecs/sim-production/us-east-1/app) on an image built after #6272 landed on main:

  • POST to deploy paths in the last 22h: 211x 200, 2x 400, 0x 5xx
  • Zero hits for before initialization / TRIGGER_REGISTRY over Aug 4-6, in production or staging

So the crash fires under Bun's ESM loader and (per the PR description) Next dev, but the production webpack build happens to order the modules favourably. That is exactly the fragility blocks/registry.ts already documents in its anyPreviewBlocks comment: "It only worked under Turbopack because that bundler happened to order the modules favourably, which is not a guarantee to build on." Same trap, second time.

Why the guard was replaced rather than kept

Two problems with fixing it at the call site:

  1. It is invisible to the test suite.deploy.test.ts passes 18/18 against the broken code — I ran it on unpatched staging. It does vi.mock('@/triggers') and vi.mock('@/blocks/registry'), and vitest.setup.ts:114 mocks @/blocks/registry globally anyway. The "29 passed" in the PR description is not evidence about this bug, and no test could have been added that would fail.
  2. It is one cleanup away from breaking again — a bare side-effect import 40 lines from the import it protects, which is precisely how it broke the first time.

The structural fix

There were 13 static triggers/ -> blocks/ edges, not one. I found them by cutting them one at a time and watching the next appear, then enumerated them properly. Two causes, both layering violations:

  • triggers/index.ts imported the mock-payload generator from trigger-utils, which imports @/blocks for unrelated helpers. The generator is pure, so it moved to lib/workflows/triggers/mock-payload.ts.
  • Eleven trigger modules (gmail/outlook/hubspot/imap/table pollers, four webflow triggers, clickup/subblocks, sim/workspace-event) statically imported the editor's Zustand stores to read sub-block values inside fetchOptions/fetchOptionById. Those now go through triggers/editor-state.ts, which loads the stores with a dynamic import() — resolved when the resolver runs, not during module evaluation, so it carries no ordering obligation. Every call site was already async.

With the cycle gone the ordering guard is unnecessary, so deploy.ts is back to its #6272 state.

Bundle side effect:@/triggers drops from 744 statically reachable modules to 526. The block registry, the workflow Zustand stores and their React Query graph are no longer dragged into every server module that imports a trigger.

Guard that can actually fail

scripts/check-trigger-block-cycle.ts (wired into check:trigger-block-cycle and CI) walks static import + re-export edges from both trigger barrels and fails on any path into blocks/, printing the shortest chain. Dynamic import() is deliberately not walked — it does not participate in initialization order.

Verified it fails on the broken code by reverting one edge:

✗ triggers/index.ts can statically reach blocks/:
triggers/index.ts
-> lib/workflows/triggers/trigger-utils.ts
-> blocks/index.ts

Verification

  • All 1,007 API routes probed under Bun: 0 crashes (was 11)
  • ESM simulator over 2,961 modules: 0 (was 15)
  • bunx tsc --noEmit: 0 errors
  • vitest run triggers/ lib/workflows/triggers/ stores/workflows/: 237 passed
  • deploy.test.ts + webhook-url.test.ts + workspace-event.test.ts: 47 passed
  • check:trigger-block-cycle, check:client-boundary, check:tool-registry-boundary, check:utils, check:api-validation: pass

Not verified: the editor dropdowns whose resolvers changed (Gmail labels, Outlook folders, HubSpot properties/pipelines, IMAP mailboxes, Webflow sites/collections, ClickUp workspaces, table columns, Sim workspace-event workflow picker). The reads are behaviour-preserving and typecheck, but they are UI callbacks I have not exercised in a browser — worth a click-through before merge.

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@cursor review

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c9aa3ec. Configure here.

@waleedlatif1
waleedlatif1 merged commit b04fee8 into stagingAug 6, 2026
7 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/deploy-trigger-registry-cycle branch August 6, 2026 22:02
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@BillLeoutsakosvl346@waleedlatif1