diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/chat-context-kind-registry/chat-context-kind-registry.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/chat-context-kind-registry/chat-context-kind-registry.tsx
index c599b5e197a..233b0e8d202 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/chat-context-kind-registry/chat-context-kind-registry.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/chat-context-kind-registry/chat-context-kind-registry.tsx
@@ -14,7 +14,7 @@ import { AgentSkillsIcon, McpIcon } from '@/components/icons'
import { getDocumentIcon } from '@/components/icons/document-icons'
import type { ChatContextKind, ChatMessageContext } from '@/app/workspace/[workspaceId]/home/types'
import { getBareIconStyle } from '@/blocks/icon-color'
-import { registry as blockRegistry } from '@/blocks/registry'
+import { getBlockRegistry } from '@/blocks/registry'
interface RenderIconArgs {
context: ChatMessageContext
@@ -41,7 +41,7 @@ function renderWorkflowIcon({ className }: RenderIconArgs): ReactNode | null {
function renderIntegrationTile({ context, className }: RenderIconArgs): ReactNode | null {
if (context.kind !== 'integration') return null
if (!context.blockType) return null
- const block = blockRegistry[context.blockType]
+ const block = getBlockRegistry()[context.blockType]
if (!block) return null
const Icon = block.icon
return
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-mention-data.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-mention-data.ts
index d0d0a1a43a9..2736c964f75 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-mention-data.ts
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-mention-data.ts
@@ -192,7 +192,8 @@ export function useMentionData(props: UseMentionDataProps): MentionDataReturn {
// Fetch current blocks from store
const workflowStoreBlocks = useWorkflowStore.getState().blocks
- const { registry: blockRegistry } = await import('@/blocks/registry')
+ const { getBlockRegistry } = await import('@/blocks/registry')
+ const blockRegistry = getBlockRegistry()
const mapped = Object.values(workflowStoreBlocks).map((b: any) => {
const reg = (blockRegistry as any)[b.type]
return {
diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts
index d87b685347a..6d1170e5198 100644
--- a/apps/sim/blocks/registry.ts
+++ b/apps/sim/blocks/registry.ts
@@ -24,8 +24,24 @@ export function getBlock(type: string): BlockConfig | undefined {
return BLOCK_REGISTRY[type] ?? BLOCK_REGISTRY[normalizeType(type)] ?? resolveOverlayBlock(type)
}
-/** Whether any registered block is an unreleased `preview` block. Static — computed once. */
-const HAS_PREVIEW_BLOCKS = Object.values(BLOCK_REGISTRY).some((block) => block.preview)
+/**
+ * Whether any registered block is an unreleased `preview` block. Computed once,
+ * on first use rather than at module scope.
+ *
+ * `blocks/registry-maps` and this module sit in an import cycle (a block config
+ * reaches back here through `providers/utils` → `tools/params` → `blocks/index`),
+ * so whichever module the cycle is entered through evaluates first. Reading
+ * `BLOCK_REGISTRY` at module scope therefore threw
+ * `ReferenceError: Cannot access 'BLOCK_REGISTRY' before initialization` for
+ * anything that imported `blocks/registry-maps` directly — scripts and tests
+ * under Bun. It only worked under Turbopack because that bundler happened to
+ * order the modules favourably, which is not a guarantee to build on.
+ */
+let hasPreviewBlocks: boolean | undefined
+function anyPreviewBlocks(): boolean {
+ hasPreviewBlocks ??= Object.values(BLOCK_REGISTRY).some((block) => block.preview)
+ return hasPreviewBlocks
+}
/**
* True when the visibility projection cannot change any block, so accessors can
@@ -33,7 +49,7 @@ const HAS_PREVIEW_BLOCKS = Object.values(BLOCK_REGISTRY).some((block) => block.p
* even with a null state) and no kill-switch entries apply.
*/
function visibilityInert(vis: BlockVisibilityState | null): boolean {
- if (HAS_PREVIEW_BLOCKS) return false
+ if (anyPreviewBlocks()) return false
return vis === null || vis.disabled.size === 0
}
@@ -232,9 +248,16 @@ export function getSuggestedSkillsForBlock(type: string): readonly SuggestedSkil
/**
* Raw block registry map keyed by block type. Prefer the typed accessors
- * (`getBlock`, `getAllBlocks`, `getCanonicalBlocksByCategory`); this alias is
- * retained for callers that need the underlying record directly.
+ * (`getBlock`, `getAllBlocks`, `getCanonicalBlocksByCategory`); this is retained
+ * for callers that need the underlying record directly.
+ *
+ * A function rather than an eager `const` alias: binding `BLOCK_REGISTRY` at
+ * module scope re-introduces the initialization-order failure that
+ * {@link anyPreviewBlocks} documents, since this module can evaluate before
+ * `blocks/registry-maps` has finished.
*/
-export const registry: Record = BLOCK_REGISTRY
+export function getBlockRegistry(): Record {
+ return BLOCK_REGISTRY
+}
export type { BlockCategory }
diff --git a/apps/sim/lib/copilot/chat/process-contents.ts b/apps/sim/lib/copilot/chat/process-contents.ts
index 54fce5d321c..1d5096fa84d 100644
--- a/apps/sim/lib/copilot/chat/process-contents.ts
+++ b/apps/sim/lib/copilot/chat/process-contents.ts
@@ -545,7 +545,8 @@ async function processBlockMetadata(
return null
}
- const { registry: blockRegistry } = await import('@/blocks/registry')
+ const { getBlockRegistry } = await import('@/blocks/registry')
+ const blockRegistry = getBlockRegistry()
if (!(blockRegistry as any)[blockId]) {
return null
}