Vercel Zero: The Programming Language Built So AI Agents Can Read, Repair, And Ship Native Code

I burned forty minutes of Claude’s context window last Tuesday parsing the same Rust compiler error.

The agent had written a function that took a &str where the caller passed a String. The borrow checker did what borrow checkers do. The error message was three paragraphs long, included a diagram of two coloured arrows, and referenced four different rules from the reference manual. The agent read the error. The agent tried a fix. The fix introduced a different error. The agent read that one. The agent tried again. By the fifth round the context was full of partial diagnostics, the model had started hallucinating crate names, and I closed the loop manually with a one-character change.

I was thinking about that loop when I saw Vercel Labs ship Zero on May 15. A new systems language whose entire pitch is that the compiler is supposed to talk to agents. Not to me. To the agent. The errors come out as JSON with stable codes. The fixes come out as machine-readable plans. The standard library declares its side effects in function signatures so an agent can audit what a binary touches without running it.

It is the kind of project that sounds gimmicky for about ninety seconds and then starts to make sense. This is the post I wish I had read on day one. What Zero actually is, where it fits, the parts that are clever, the parts that are still vapour, and whether you should bother installing it this week.


What Zero Actually Is

Zero is an experimental systems language from Vercel Labs, currently at v0.1.2, Apache 2.0 licensed, with source files using the .0 extension. The compiler is written partly in C (the bootstrap, in native/zero-c/) and partly in Zero itself (the self-hosted compiler, in compiler-zero/). It emits native binaries for Linux, macOS, and Windows, plus WebAssembly, without going through LLVM.

The headline number is that a “hello world” binary lands under ten kilobytes. Not ten megabytes. Not even ten hundred kilobytes. Sub-ten KiB. That is closer to a Zig “hello world” than a Rust one, and the absence of LLVM is the reason. Compile times stay short. Binaries stay tiny. The tradeoff is that the optimiser is not yet competitive with what LLVM has been polishing for two decades, so for pure compute-bound code Rust will still win. Zero is not trying to win that race.

The thing Zero is trying to win is a different one. Most languages were designed for humans to write and humans to read. Their compilers were designed for humans to debug. The error messages, the documentation, the package ecosystems, the IDE tooling are all shaped around a human in the loop. Zero is the first language I have seen that was designed, on purpose, from day one, for a workflow where the human is not necessarily the one reading the compiler output. The agent is.

That single design choice changes a surprising number of downstream decisions, and most of this post is about what those decisions actually look like in the code.


Why A New Language At All

The honest version of “why a new language” is that you cannot retrofit agent-first tooling onto an existing language without losing the design. You can add a --json flag to rustc. You can write a wrapper that parses tsc output. You can pipe gcc errors through an LLM and ask it to extract structured fields. People are doing all three. None of it really works.

It does not work because the underlying compilers were not designed to make stable promises to a machine. Error message wording changes between versions. Span information moves around. The mental model the human reading the error is supposed to apply lives in prose, not in a typed payload. The agent has to do an extra interpretation step every time it reads the error, and that interpretation costs tokens, costs accuracy, and costs the kind of loop reliability that turns “agentic coding works on demos” into “agentic coding ships features.”

I wrote about this exact failure mode in agentic coding in 2026. The agent is fine when the loop is short. The agent collapses when the loop has more than three or four rounds because the diagnostic surface is too noisy. Zero’s bet is that if you remove the prose-parsing step, the loop tightens enough that the agent stays coherent for longer.

The other half of the bet is that the rest of the toolchain has to change too. It is not enough for the compiler to emit JSON errors. The package manager has to emit JSON errors. The formatter has to. The doc generator has to. The size analyser, the import graph, the cross-compile target list, all of it has to be structured. Otherwise the agent still has to parse three different output formats, and you have only fixed one third of the problem.

Zero’s single binary ships every subcommand from one process: zero check, zero run, zero build, zero size, zero graph, zero routes, zero fix, zero explain, zero doctor, zero skills. They all share a --json flag. They all share the same diagnostic schema. The agent learns one shape, not ten.


Hello World, Then The Trick

A Zero “hello world” looks like this.

pub fun main(world: World) -> Void raises {
  check world.out.write("hello from zero\n")
}

There is one thing on that page that does not exist in any mainstream language: the world: World parameter. Every function that wants to touch the outside world, write to stdout, open a file, hit the network, has to accept a World capability. A pure function does not. A function that only adds two integers looks like this.

fun answer() -> i32 {
  return 40 + 2
}

No world parameter. No raises. The signature is the contract. If you read fun answer() -> i32 you know, statically, that calling it cannot touch a file, cannot make a network request, cannot block on a queue, cannot leak data. The compiler enforces it. If answer tried to call world.out.write it would not compile, because answer does not have a World to pass to it.

The pattern is called capability-based I/O, and it has been kicking around the academic side of language design for thirty years. It is not new. What is new is shipping it as the default in a language meant for production work, not as an opt-in linter you turn on for security-critical files.

The reason this matters in 2026, specifically, is auditability. When an agent generates a Zero function, you can read the signature alone and know what it can do. No execution. No deep code analysis. No “did the agent quietly add a fetch call buried inside a helper?” The capability has to be passed in. If it is not in the signature, the function cannot do the thing.

The same instinct that drives sandboxing AI-generated code at the runtime layer drives capability-based I/O at the language layer. The runtime version says “this process cannot reach the network.” The language version says “this function cannot reach the network.” They compose. They do not replace each other.


The Compiler That Talks JSON

The headline feature is the JSON diagnostic output. Run zero check --json some_file.0 and instead of a paragraph of prose you get a structured payload that looks roughly like this.

{
  "diagnostics": [
    {
      "code": "NAM003",
      "severity": "error",
      "message": "unknown identifier `wirte`",
      "span": {
        "file": "examples/hello.0",
        "line": 2,
        "column": 23
      },
      "repair": {
        "id": "declare-missing-symbol",
        "candidates": ["write"]
      }
    }
  ]
}

Three things matter here, and only one of them is the JSON itself.

The first thing is the stable code. NAM003 is going to mean “unknown identifier” in v0.1, in v0.2, in v1.0, and forever after. Vercel has committed to keeping the codes stable across versions. That means an agent can have a built-in mental model of “code starting with NAM is a naming problem” and apply it across compiler releases. The wording of the message is allowed to change. The code is not.

The second is the typed repair. The repair.id is declare-missing-symbol, and it comes from a finite, documented vocabulary. The agent does not have to read the message to know what kind of fix is needed. It can look up the repair id and apply a known transformation. For the small set of mechanical errors (typos, missing imports, missing semicolons), the agent does not need to reason. It just has to look up the fix template.

The third, and this is the part that surprised me, is zero fix --plan --json. Run it against the same file and the compiler returns a fix plan, not a fix. It tells the agent “I would change line 2 column 23 from wirte to write,” and lets the agent decide whether to apply it. The plan is the diff, structured. The agent can accept it, modify it, or reject it. It is the difference between a code action you blindly run and a code action you negotiate with.

The agent loop ends up looking like this.

zero check --json
  -> diagnostics with stable codes
zero fix --plan --json
  -> machine-readable repair plan
agent reviews plan, applies it
zero check --json
  -> clean (hopefully)

That loop is short. That loop does not eat the model’s context window. That loop is the reason Zero exists.


The CLI Is One Binary, And That Matters More Than It Sounds

Most languages have separate binaries for separate jobs. rustc, cargo, rustfmt, clippy, rust-analyzer. go, gofmt, golangci-lint. tsc, eslint, prettier, tsx. Every one of them has its own flag conventions, its own exit codes, its own output formats, its own update cadence. For a human this is mostly fine, because the human only invokes one of them at a time and remembers the flags by muscle memory.

For an agent it is a nightmare. The agent has to learn ten different command surfaces, ten different output parsers, ten different version compatibility matrices. Half of the agent’s failures in agentic coding are not “the agent cannot reason about the code.” They are “the agent invoked the wrong tool with the wrong flag and could not parse the resulting output.”

Zero collapses this into one binary. The subcommands.

  • zero check validates a file or a package
  • zero build produces a binary (with --target for cross-compilation and --emit to choose the output)
  • zero run runs a file directly
  • zero size --json reports the binary size, broken down by module
  • zero graph --json emits the import graph as JSON
  • zero routes --json extracts HTTP routes from a web package
  • zero fix --plan --json returns a repair plan for the current diagnostics
  • zero explain CODE returns a typed explanation for a diagnostic code
  • zero doctor checks the local install
  • zero skills get zero --full returns the version-matched usage guide

The last one is doing something quiet but useful. Documentation in most ecosystems lives on a website that may or may not match the version on your machine. Zero ships the workflow guides inside the binary, version-locked. The agent does not have to web-search “how do I cross-compile to musl in Zero v0.1.2?” and get a v0.0.7 Stack Overflow answer. It runs zero skills get cross-compile and gets the answer that matches the installed compiler.

This is the same pattern as MCP servers shipping their own tool guides, one level lower in the stack. The tool documents itself to the agent, with no out-of-band fetch.


Memory Without A Garbage Collector

Zero is a systems language, not a scripting language, and that shows up most clearly in how it handles memory. There is no garbage collector. There is no hidden allocator. Every allocation is explicit, every deallocation is too, and the type system tracks the lifetime of references through a borrow-checker-style mechanism.

If you have written Rust this will feel familiar. If you have written Zig it will feel even more familiar, because the explicitness leans closer to Zig’s “the allocator is a parameter you pass in” model than Rust’s “the allocator is implicit but the lifetimes are not.”

The borrow checker in Zero v0.1.2 is, by all reports, less sophisticated than Rust’s. It handles the easy cases, the ones that prevent use-after-free and double-free and the simple aliasing bugs. It does not yet handle the harder cases that take real effort to model in Rust. That gap will close over time. For now, Zero is not trying to be a Rust replacement. It is trying to be a language where binaries are small, allocations are visible, and an agent can read the size report after each build and notice when something doubled.

That last bit, the per-build size report, is more important than it looks. The most common failure I have seen with AI-generated code shipping to production is not a bug in the logic. It is a quiet ten-megabyte regression that nobody noticed until the cold start time on the function tripled. Zero’s zero size --json ships a structured size report for every module in the binary. The agent can diff sizes between commits and flag bloat the moment it appears. That kind of automated audit is the kind of thing you cannot easily do in Rust without a custom toolchain. In Zero it is a subcommand.


What Zero Is Not

Zero is not a Rust replacement. It is missing the borrow checker maturity, the package ecosystem, the IDE tooling, the production track record, and the community that took Rust a decade to build. If you need to ship a database, a browser engine, an operating system kernel, or any other “this code will run for ten years with no rewrite” target, you do not pick a v0.1.2 language.

Zero is not a Go replacement either. It does not have green threads. It does not have channels as a built-in primitive. The standard library is much smaller. The deployment story is “you get a binary,” which is what Go also gives you, but Go is a five-megabyte binary with a complete runtime and Zero is a ten-kilobyte binary without one. Different tradeoffs, different fit.

Zero is not a Python replacement, a Node replacement, or a TypeScript replacement. It is a systems language. It is not what you reach for to build a web app, a data pipeline, or a CRUD service.

Zero is also not, despite the marketing, “the language for agents” in the sense that you should write all your AI-related code in it. The runtime around an agent (the prompt orchestration, the tool calls, the memory layer, the eval pipeline) still belongs in whatever language already has a working SDK. Zero is for the parts of the stack where an agent needs to produce a small, fast, auditable binary. Think CLI utilities, edge functions, glue tools, embedded scripts, generated microservices. Not your main application code.

What Zero is is a working experiment in agent-native toolchain design. It is the first language whose compiler, package layout, error format, and CLI were all designed at the same time, with an agent in mind. Whether the experiment succeeds in the long run depends on whether enough agent frameworks bother to learn the Zero conventions specifically, or whether Zero’s conventions get cribbed by larger languages, or whether the whole thing turns out to be an answer to a question nobody actually had.

The honest answer is that it is too early to tell. v0.1.2 is not a verdict. It is a start.


The Skeptical Read

I want to give the skeptical version equal time, because if you only read the marketing you would think Vercel solved the agent loop problem. They did not. They built a clever tool that solves one slice of it.

The first skeptical point: JSON diagnostics are not new. Rust has JSON error output, TypeScript can emit machine-readable diagnostics, Go’s go vet is parseable. The reason agents do not lean on those today is not “the format does not exist.” It is that the agent frameworks have not standardised on a way to consume them. Zero solving the producer side does not automatically solve the consumer side.

The second skeptical point: capability-based I/O is great for auditability and lousy for ergonomics if the rest of the standard library does not lean into it. If every helper function in std requires you to thread a World parameter through six layers of call stack, the prose-code ratio gets bad fast. Languages that have tried this approach (Koka, Eff, various effect-system experiments in the academic world) ran into exactly this wall. Zero’s bet is that the agent doing the threading does not care about ergonomics, and the human is mostly reviewing, not writing. That bet is probably right, but it does mean Zero is unlikely to feel pleasant to write by hand for a human.

The third skeptical point: stable diagnostic codes are a promise, and promises break. Rust has changed error wording multiple times. Even with stable codes, the meaning of a code can drift as the language evolves. If the agent has memorised “code NAM003 means typo, apply fix template X” and a future Zero version uses NAM003 for something subtly different, the agent will quietly do the wrong thing. This is not unique to Zero, but the agent-first framing makes it a higher-stakes commitment than the same promise in Rust.

The fourth skeptical point: there is no package registry yet. Every serious systems language has had to solve the dependency problem, and the answer is always painful. Cargo took Rust years to get right. go mod is still controversial. Zero will have to ship some version of this and the design choices will determine whether the language scales to real projects or stays a hobbyist toy. v0.1.2 has not made those decisions yet, which means anyone betting on Zero today is betting on a future package manager that does not exist.

None of these points kill the project. They just frame what “Zero is interesting” means in 2026: it is a research artifact with a working compiler and a clear design thesis, not a production tool you should pick for the next thing you build.


Should You Install It

Yes, if you are curious. No, if you have a deadline.

The install is one line.

curl -fsSL https://zerolang.ai/install.sh | bash
export PATH="$HOME/.zero/bin:$PATH"
zero new cli hello
cd hello
zero run

You will have a sub-ten-kilobyte binary that prints “hello from zero” in about a minute. That is enough to decide whether the design thesis appeals to you. If it does, the rest is reading the examples/ directory in the repo and the conformance/ test fixtures, both of which are well-curated.

The audience worth recommending Zero to specifically is people building agent frameworks. If you are wiring up Claude or GPT to write code in a loop, Zero is a target language worth experimenting with right now, because the diagnostic surface is built for what you are trying to do. You will find out, fast, whether the JSON loop actually reduces the round trips for your agent. If it does, you can advocate for the format to propagate into the languages your team already uses.

The audience worth telling not to switch yet is anyone shipping production code. The borrow checker is too young, the ecosystem is empty, and the language spec is not stable. You will hit edges. The edges will not be fun. Come back in a year.


What I’d Tell Past Me

If I could send one paragraph back to last Tuesday, mid-context-collapse with the Rust borrow checker, it would be this. The problem you are watching is not a Claude problem. It is a language problem. The languages we have were built for humans who could read three paragraphs of prose and parse a coloured-arrow diagram. The agents we have were not. Until either the languages change or the agents get much smarter at parsing prose, the loop will keep eating context windows.

Vercel’s bet with Zero is that the languages can change faster than the agents will. The bet might be right. The bet might be wrong. The interesting part is that someone is actually making it.

Whether you use Zero in the next year or not, the design pattern (JSON diagnostics, typed repairs, capability signatures, a single CLI binary, version-locked agent guidance) is going to show up in the next generation of tooling whether or not Zero itself catches on. The ideas are loose now. They will not stay loose for long.

Install it once. Read the examples. File the design thesis away. Then go back to whatever language is paying your bills and watch which of these ideas show up there first.

That is usually how this stuff works. The experiment gets the attention. The convention wins.