different-ai/openwork · error · InterpreterRuntimeError

Unsupported for...in binding.

Error message

Unsupported for...in binding.

What it means

The for...in left side must be a single VariableDeclaration or a plain Identifier. Any other node kind on the left (member expressions such as `obj.k`, unsupported pattern nodes) throws this error at the left node. Assignment targets inside loop heads are limited to these two forms by the interpreter.

Source

Thrown at packages/codemode/src/interpreter/runtime.ts:1199

          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...in 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...in binding.", left)
      }

      for (const key of keys) {
        if (declaration) {
          self.pushScope()
          yield* self.declarePattern(declaration.pattern, key, declaration.mutable, left)
        } else if (assignmentName) {
          self.setIdentifierValue(assignmentName, key, left)
        }

        const result = yield* self.evaluateStatement(body).pipe(
          Effect.ensuring(
            Effect.sync(() => {
              if (declaration) self.popScope()
            }),
          ),
        )

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Declare `for (const k in src)` and assign to the member inside the body: `target[k] = ...`.
  2. Use a pre-declared plain identifier as the loop variable.
  3. Fix the AST generator to only emit Identifier or single-declarator VariableDeclaration left sides.

Example fix

// before
for (out[key] in src) { ... }
// after
for (const key in src) { out[key] = src[key]; }
Defensive patterns

Strategy: validation

Validate before calling

// AST check before execution
const t = forInNode.left.type;
if (t !== "VariableDeclaration" && t !== "Identifier") {
  throw new Error("for...in target must be a declaration or identifier");
}

Type guard

const isValidForInLeft = (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...in binding")) {
    // rewrite to `for (const k in src)` with in-body assignment
  }
  throw e;
}

Prevention

When it happens

Trigger: `for (obj.key in source)` or any for...in whose left AST node is neither VariableDeclaration nor Identifier.

Common situations: Generated CodeMode code that tries to write each key into an object property directly in the loop head.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/fe41ee052bfa7c0e. Report an issue: GitHub.