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), soView on GitHub (pinned to 2b7df46e8a)
Solutions
- Remove the stray break/continue statement
- Ensure the statement is nested inside a for/while loop
- 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
- Ensure every break/continue sits inside a for/while loop
- Prefer return or conditionals over control-flow jumps at top level
- Review refactors that remove loops for leftover break/continue
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
- for...of supports one declared binding.
- Unsupported for...of binding.
- 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/fc2ad4b4c4fe451e.
Report an issue: GitHub.