different-ai/openwork · error · InterpreterRuntimeError

Unexpected '${result.kind}' outside of a loop.

Error message

Unexpected '${result.kind}' outside of a loop.

What it means

When the interpreter's run loop finishes executing a statement, a break or continue result escaping to top level (not inside a loop) is invalid control flow. The interpreter detects this and throws, mirroring JavaScript's own SyntaxError for stray break/continue.

Source

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

    // Run the program body in its own module scope on top of the builtin global scope, so
    // top-level declarations (`let undefined = 5`, `const Object = ...`) shadow builtins like
    // JS module scope, instead of colliding with the seeded globals.
    this.pushScope()
    return Effect.gen(function* () {
      self.hoistFunctions(program.body)
      let value: unknown = undefined
      let returned = false
      for (const statement of program.body) {
        const result = yield* self.evaluateStatement(statement)

        if (result.kind === "return") {
          value = result.value
          returned = true
          break
        }

        if (result.kind === "break" || result.kind === "continue") {
          throw new InterpreterRuntimeError(`Unexpected '${result.kind}' outside of a loop.`, statement)
        }

        if (result.kind === "value") {
          self.lastValue = result.value
        }
      }
      if (!returned) value = self.lastValue

      // The program body runs inside an implicit async function, so a returned promise
      // resolves before crossing the data boundary - `return tools.ns.tool(...)` works
      // without an explicit await, exactly as in JS.
      if (value instanceof SandboxPromise) value = yield* self.settlePromise(value)
      yield* self.drainPendingSettlements()
      return value
    }).pipe(Effect.ensuring(Effect.sync(() => self.popScope())))
  }

  // Awaits every fiber-backed promise the program abandoned (fire-and-forget tool calls), so

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Remove the stray break/continue statement
  2. Ensure the statement is nested inside a for/while loop
  3. Replace the early exit with a conditional or return

Example fix

// before
if (done) break;
// after
if (done) return; // or restructure inside a loop
Defensive patterns

Strategy: validation

Validate before calling

// crude check: a break/continue not preceded by a loop keyword in the same block scope
for (const m of code.matchAll(/\b(break|continue)\b/g)) {
  const before = code.slice(0, m.index);
  if (!/(for|while)\s*\(/.test(before.split('}').pop() ?? '')) {
    throw new Error(`'${m[1]}' may be outside of a loop`);
  }
}

Try / catch

try {
  result = invokeCodeMode(code);
} catch (e) {
  if (String(e.message).includes("outside of a loop")) {
    // flag the stray break/continue for rewrite
  } else throw e;
}

Prevention

When it happens

Trigger: A break or continue statement positioned outside any loop body in CodeMode code — e.g. top-level break, or break inside an if that is not enclosed by a for/while the interpreter recognizes, including break inside non-loop constructs like function bodies at top level.

Common situations: Hand-written or LLM-generated code with a mis-nested break/continue; refactoring that removed the enclosing loop but left the break; translating code from languages with labeled block breaks.

Related errors


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