different-ai/openwork · error · InterpreterRuntimeError
Unsupported for...of binding.
Error message
Unsupported for...of binding.
What it means
The for...of left side must be either a single VariableDeclaration or a plain Identifier (assignment to an existing variable). Any other left-side node kind (member expression like `obj.x`, complex patterns beyond the supported ones, etc.) throws this error at the left node. The interpreter implements only these two binding forms.
Source
Thrown at packages/codemode/src/interpreter/runtime.ts:1106
if (iterable === undefined) {
throw new InterpreterRuntimeError("for...of requires an array, string, Map, or Set value in CodeMode.", node)
}
let declaration: { readonly pattern: AstNode; readonly mutable: boolean } | undefined
let assignmentName: string | undefined
if (left.type === "VariableDeclaration") {
const declarations = getArray(left, "declarations")
if (declarations.length !== 1) {
throw new InterpreterRuntimeError("for...of supports one declared binding.", left)
}
const declarator = asNode(declarations[0], "declarations[0]")
declaration = { pattern: getNode(declarator, "id"), mutable: getString(left, "kind") !== "const" }
} else if (left.type === "Identifier") {
assignmentName = getString(left, "name")
} else {
throw new InterpreterRuntimeError("Unsupported for...of binding.", left)
}
for (const value of iterable) {
if (declaration) {
self.pushScope()
yield* self.declarePattern(declaration.pattern, value, declaration.mutable, left)
} else if (assignmentName) {
self.setIdentifierValue(assignmentName, value, left)
}
const result = yield* self.evaluateStatement(body).pipe(
Effect.ensuring(
Effect.sync(() => {
if (declaration) self.popScope()
}),
),
)
View on GitHub (pinned to 2b7df46e8a)
Solutions
- Declare a loop variable, then assign to the member inside the body: `for (const v of items) { obj.key = v; }`.
- Use an existing plain variable as the loop target instead of a property path.
- If a pattern is needed, keep it to destructuring supported by declarePattern inside a single VariableDeclaration.
Example fix
// before
for (obj.key of items) { ... }
// after
for (const v of items) { obj.key = v; } Defensive patterns
Strategy: validation
Validate before calling
// AST check before execution
const t = forOfNode.left.type;
if (t !== "VariableDeclaration" && t !== "Identifier") {
throw new Error("for...of target must be a declaration or identifier");
} Type guard
const isValidForOfLeft = (n: AstNode): boolean => n.type === "Identifier" || (n.type === "VariableDeclaration" && Array.isArray(n.declarations) && n.declarations.length === 1);
Try / catch
try {
interpret(src);
} catch (e) {
if (e instanceof InterpreterRuntimeError && e.message.includes("Unsupported for...of binding")) {
// rewrite to declare + in-body assignment
}
throw e;
} Prevention
- Assign to object properties inside the loop body, not the head.
- Restrict generated loop heads to Identifier or single-declarator declarations.
- Validate AST loop nodes before invoking the interpreter.
When it happens
Trigger: `for (obj.key of items)`, `for (arr[0] of items)`, or any AST left node that is neither VariableDeclaration nor Identifier.
Common situations: Porting JS that assigns to an object property per iteration; LLM-generated ASTs using unsupported node kinds for the loop target.
Related errors
- for...of supports one declared binding.
- for...of requires an array, string, Map, or Set value in Cod
- for...in supports one declared binding.
- Unsupported for...in binding.
- Expected '${key}' to be a boolean.
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/413b79fafe62f855.
Report an issue: GitHub.