Why this fires¶
The program references a name that is part of the VM's always-in-scope prelude (declared in the bundled .d.zolo registries — the same declaration table the VM and the LSP consult), but the native/LLVM backend has no implementation for it yet. Building it anyway would not produce an error at runtime: the name would silently evaluate to nil, a call on it would silently return nil, a field read on that would return nil again — and the program would print nil and exit 0 with zero signal that anything went wrong. TE134 fails the build at the first layer instead.
setTimeout(|| { print("later") }, 100)$ zolo build app.zolo
Build error: error[TE134]: `setTimeout` exists in the VM prelude (`scheduler` group) but has no native implementation yet — on the native/LLVM backend it would silently evaluate to nil, so the build fails instead.The same program still runs on the VM:
$ zolo run app.zolo # works — the VM prelude implements setTimeoutTE134 only fires when all of these hold:
- The build targets the native (Cranelift) or LLVM backend (
zolo build). - The identifier resolves to nothing in your program: it isn't a local, parameter, capture, top-level
let, function, struct, enum, or enum variant. Your own binding with the same name always wins and never triggers this. - The name is declared in an always-in-scope prelude group (
base,scheduler,reactive,fetch,comptime,async,plugin,traits). Opt-in groups (e.g.testing) are excluded — the VM wouldn't resolve those on a default run either. - The native backend has no bridge for that use. Bridged surface keeps building:
print,tostring, and — in call position —tick,block_on,channel,typeinfo,signal,effect,panic,catch_panic,sleep,spawn/scope/selectforms.
Genuinely unknown names (typos, plugin globals, cross-module runtime globals) are not affected: they keep the pre-existing behavior (runtime lookup with plugin-registry fallback).
Fix it¶
1. Run on the VM¶
If the program legitimately needs the VM-only feature (setTimeout, fetch, computed, pairs, …), run it with zolo run instead of building natively. The diagnostic exists precisely to tell you this at compile time instead of shipping a nil-bomb.
2. Use the natively-supported equivalent¶
Several VM prelude names have first-class native counterparts:
setTimeout(f, ms)→after ms { ... }orspawn { tick(ms); f() }setInterval(f, ms)→every ms { ... }- Lua-style
pairs(t)iteration →for k in t.keys() { ... }/ collection methods
3. Shadow it with your own binding¶
If you intended a symbol of your own, declare it — user bindings always take priority over the prelude table:
fn fetch(url) { ... } // your own fetch — TE134 no longer appliesSee also¶
TE100— unknown name (the typo class; caught by the type checker on the VM path).- /docs/async-concurrency — the natively-supported
spawn/tick/every/afterconcurrency surface.