-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Description
Bug Description
When function/condition blocks reference undefined block variables (e.g., <webhook1.conversation.id> when webhook block hasn't executed), the variable reference is substituted into the code but the actual variable is never injected into the execution context, causing "ReferenceError: variable is not defined" at runtime.
This issue was discovered as a consequence of fixing #2794 - while the nullish coalescing fix addressed the substitution syntax, it revealed this deeper IPC serialization bug.
Root Cause
The variable substitution process has a multi-layer issue:
- Code Substitution Layer:
<webhook1.conversation.id>is correctly replaced with__tag_webhook1_conversation_idin the code - Context Variable Storage: The variable IS added to
contextVariablesmap withundefinedvalue - IPC Serialization Layer: When
contextVariablesis serialized via IPC to the worker thread,JSON.stringify()strips keys with undefined values, causing those variables to not be transmitted - Execution Layer: The worker receives an empty
contextVariablesobject and can't inject the missing variables into the VM
Result: Code references __tag_webhook1_conversation_id but it's never defined in the execution context.
Error Manifestation
ReferenceError: __tag_webhook1_conversation_id is not defined
This occurs in:
- E2B JavaScript execution path
- E2B Python execution path
- Isolated-VM execution path
Reproduction Steps
- Create a workflow with Start trigger and Webhook trigger
- Add Extract Context function block with:
const x = <webhook1.optional_field> ?? "default"; - Execute workflow using Start trigger (webhook doesn't execute)
- Function block fails with "ReferenceError: __tag_webhook1_optional_field is not defined"
Root Issue: IPC Serialization
The key problem occurs in apps/sim/lib/execution/isolated-vm.ts:
// BROKEN - undefined values are stripped during JSON serialization in IPC
worker!.send({ type: 'execute', executionId, request: req })When req.contextVariables contains { __tag_webhook_id: undefined }, the IPC serialization strips it, resulting in an empty object on the worker side.
Solution Applied
File 1: apps/sim/lib/execution/isolated-vm.ts
Add explicit sanitization BEFORE IPC transmission:
+ // Convert undefined values to null in contextVariables before IPC serialization.
+ // JSON.stringify() strips keys with undefined values, which would cause ReferenceErrors
+ // when user code references variables that weren't properly injected into the VM context.
+ const sanitizedContextVariables: Record<string, unknown> = {}
+ for (const [key, value] of Object.entries(req.contextVariables)) {
+ sanitizedContextVariables[key] = value === undefined ? null : value
+ }
+ const sanitizedReq = { ...req, contextVariables: sanitizedContextVariables }
return new Promise((resolve) => {
// ...
try {
- worker!.send({ type: 'execute', executionId, request: req })
+ worker!.send({ type: 'execute', executionId, request: sanitizedReq })
} catch {File 2: apps/sim/app/api/function/execute/route.ts
Fix E2B JavaScript and Python paths:
// E2B JavaScript path (around line 745)
for (const [k, v] of Object.entries(contextVariables)) {
- prologue += `const ${k} = JSON.parse(${JSON.stringify(JSON.stringify(v))});\n`
+ // Convert undefined to null so JSON.stringify produces valid output
+ const safeValue = v === undefined ? null : v
+ prologue += `const ${k} = JSON.parse(${JSON.stringify(JSON.stringify(safeValue))});\n`
prologueLineCount++
}
// E2B Python path (around line 817)
for (const [k, v] of Object.entries(contextVariables)) {
- prologue += `${k} = json.loads(${JSON.stringify(JSON.stringify(v))})\n`
+ // Convert undefined to null so JSON.stringify produces valid output
+ const safeValue = v === undefined ? null : v
+ prologue += `${k} = json.loads(${JSON.stringify(JSON.stringify(safeValue))})\n`
prologueLineCount++
}File 3: apps/sim/lib/execution/isolated-vm-worker.cjs
Add defensive handling in worker:
for (const [key, value] of Object.entries(contextVariables)) {
- await jail.set(key, new ivm.ExternalCopy(value).copyInto())
+ // Convert undefined to null to ensure the variable is properly set in the VM
+ const safeValue = value === undefined ? null : value
+ await jail.set(key, new ivm.ExternalCopy(safeValue).copyInto())
}Why This Fix Works
This ensures:
- All variables are transmitted via IPC (null is serializable, undefined is not)
- Worker receives all variables and can inject them into the VM context
- User code can use nullish checks (
??) on the received null values
Related Issues
- [BUG] Variable substitution breaks nullish coalescing operator (??) in function blocks #2794: Variable substitution breaks nullish coalescing operator - this bug was discovered as a follow-up to that fix
Testing
Verify with:
- Start trigger + Extract Context referencing non-existent webhook fields
- Confirm
??operator fallback works correctly - Test all three execution paths (E2B JS, E2B Python, Isolated-VM)