Build errors and limits
validate_code and commit build the game and run its unit tests. This section lists what a build accepts, every error code an agent can meet, the limits of a test run, and the limits of a match.
Studio code editor

The studio's Code tab highlights Rust and saves each file automatically. After a save, Boardweaver builds the game logic, runs its unit tests, and bundles the client against the generated game types.
The build strip shows pending changes, running builds, errors and warnings. Expand it to read the diagnostics. Select a diagnostic with a file location to open that file. When Boardweaver cannot build, the strip shows a build error.
The editor does not let you rename or delete /src/lib.rs or /src/frontend.tsx. Create, rename and delete other Rust modules under /src/. The editor has no Rust autocomplete or formatting.
Run validate_code before you commit: only it type-checks the client.
Source files
- The crate root is
/src/lib.rs. Without it, the build fails withbw::no-crate-root. - Every other Rust file is a module the crate declares with
mod name;:/src/cards.rsformod cards;,/src/cards/deck.rsformod deck;insidecards. - A file no
modreaches is warningbw::unused-file. The build reports it only when the game logic builds without an error and its tests compile. - A module only the tests declare, such as
#[cfg(test)] mod tests;, counts as reached when the game has a#[test]. The build then compiles the tests too. - A
.rspath uses lowercase letters, digits and_, starts each name with a letter or_, and sits at most 7 directories below/src/. - JSON the game logic reads goes in
/src/data/(Data files). - The build also reads the catalog of the game's default locale, such as
/src/i18n/en.json, so it can check eachmsg!against it (Log lines). A change to that catalog builds the game again. A change to another locale's catalog does not. - The build takes at most 200 files, 256 KiB per file and 2 MiB in total, data files and the default catalog included.
write_file, patch_file and rename_file refuse a change the build could never accept, with UNBUILDABLE_RUST_SOURCE: a .rs or data file path outside these rules, or a file, file count or total over these limits. A change to the default catalog counts toward the limits too.
Catalogs
A game's catalogs, /src/i18n/<locale>.json, hold its messages in each locale. Their paths, limits and checks are in Translations.
Crates
A game uses exactly three crates: boardweaver_sdk, serde and serde_json. They are already in scope by name. Use serde's derive through the SDK:
use boardweaver_sdk::serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[serde(crate = "boardweaver_sdk::serde")]
struct Hand {
cards: Vec<String>,
}
There is no Cargo.toml to edit. Write Rust 1.98.1, edition 2024.
Words the build refuses
The build refuses these words anywhere in a .rs file, tests included, even as your own names:
| Refused | Write instead |
|---|---|
static, thread_local |
Keep the value in the state, or make it a const. |
unsafe |
Safe Rust. The SDK gives a game everything it needs. |
extern |
Nothing: the three crates are already in scope. |
env, option_env |
Another name, such as environment. |
include, include_str, include_bytes |
A data file read with data!, a const in the source, or a module file with mod. |
asm, global_asm, naked_asm |
Rust. |
The build also refuses mod inside a macro, #[path], #[should_panic], crate-level #![...] attributes other than allow, warn, deny, forbid, expect and doc, and brackets nested deeper than 256. The 'static lifetime is allowed.
Reading a build error
validate_code answers errors and warnings as text in the Rust compiler's format, with paths as you wrote them:
error[bw::refused]: a game may not declare a `static`
--> /src/lib.rs:12:1
|
12 | static COUNT: u32 = 0;
| ^^^^^^
= help: A game is a pure function of its state, the action and the seed, so nothing may outlive a call. Keep the value in a field of the match's state, or use a `const` or a function; for a lookup table, build a `BTreeMap` where you need it rather than caching it
Every bw::refused error ends with a help line that says what to write instead.
A failed commit answers BUILD_FAILED with the first 20 errors. Fix them from the top: later errors often follow from the first.
| Code | Means |
|---|---|
E...., a lint name |
the Rust compiler's own error or warning |
bw::refused |
a word or construct the build refuses (Words the build refuses) |
bw::lex |
a file does not read as Rust tokens |
bw::no-crate-root |
no /src/lib.rs |
bw::unused-file |
a file no mod reaches (warning) |
bw::read-outside-source |
the code refers to a file outside the game's .rs and data files |
bw::data-json |
a data file is not JSON (Data files) |
bw::module-too-large |
the built game is over 4 MiB |
bw::bad-module |
the built game exports no game: check for export_game! |
bw::test-failed |
a unit test panicked or returned an Err; the error points at the panic's line and quotes its message (Testing a game) |
bw::test-fuel |
a unit test used all its computation, or the tests used all of theirs and the rest did not finish (Test limits) |
bw::test-trap |
a unit test stopped another way, such as running out of memory, or the tests ran out of time (Test limits) |
bw::too-many-tests |
the game logic has more than 200 unit tests, or their names are over 256 KiB, so none ran |
bw::client-types |
the types listed in client_types! do not read, or their JSON Schema is over 512 KiB; the message names the problem, such as two types sharing one name or a type that refers to one no client_types! defines (Client types) |
bw::time-limit |
checking and compiling took over 180 seconds, or the whole build ran past 290 seconds |
bw::memory-limit |
the build used more than its 1 GiB of memory |
bw::truncated |
more than 200 diagnostics; only the first are shown |
Tool errors
| Code | Tool | Means |
|---|---|---|
GAME_NOT_FOUND |
every tool whose input requires gameDefinitionId, and save_checkpoint |
The game does not exist, was deleted, or your role on it does not reach the tool. Boardweaver gives all three the same answer. |
UNBUILDABLE_RUST_SOURCE |
write_file, patch_file, rename_file |
The change gives a .rs path, a data file path or a size the build can never accept, the default catalog's included (Source files). Nothing changed. |
INVALID_CATALOG |
write_file, patch_file, rename_file |
A catalog Boardweaver could never use (Catalogs). The message names the file and why. |
INVALID_THEME_JSON |
write_file, patch_file |
/theme.json is not JSON. |
INVALID_THEME |
write_file, patch_file |
/theme.json does not fit the theme's fields (Design the theme). The message names each wrong field. |
BUILD_FAILED |
commit, start_game |
The game logic did not build, a unit test failed, or a client type's name cannot be exported from boardweaver/game (Client types). For commit only: the client did not build, a catalog cannot be used, the default locale has no catalog, or a bot's text does not read (Translations). validate_code lists the same problems as errors. |
CATALOG_PLACEHOLDERS_MISMATCH |
commit |
A translation's message places other placeholders, tags or select keys than the same message in the default catalog (Translations). The message names each file, key and both sets of placeholders. Nothing was built. |
SOURCE_CHANGED |
commit |
A Rust file, a data file, a catalog, the default locale or the framework changed while the game built, so nothing was committed. Commit again. |
BUILD_TOO_NEW |
commit |
Boardweaver is part way through an update. Commit again shortly. |
RULES_COMPILE_FAILED |
commit |
A rules page did not compile. The message lists each problem, with its file and node (Write the rules pages). |
MARKETING_COMPILE_FAILED |
commit |
A marketing page did not compile, and every rules page did. The message lists each problem (Write the marketing page). |
BUILD_BUSY |
validate_code, commit, start_game |
Boardweaver is building other games. Try again after the time it names. |
BUILD_UNAVAILABLE |
validate_code, commit, start_game |
Boardweaver cannot build games right now. Try again later. |
NOT_CONFIGURED |
validate_code, commit, start_game |
This Boardweaver environment cannot build games. |
TYPE_CHECK_BUSY |
validate_code |
Boardweaver is type-checking other games. Nothing changed. Try again in a moment. |
INVALID_PLAYER_COUNT |
start_game |
numPlayers is not one of the game's validPlayerCounts, or differs from the number of players in state. |
INVALID_STATE |
start_game, set_state |
The state is not one a session can hold. The message names each problem. |
INVALID_PATH |
set_state |
An update's path does not reach a place in the state. |
STATE_NOT_FOUND |
apply_action, inspect_state, set_state, render_game, save_checkpoint |
The session does not exist or has expired. Start a new one with start_game. |
INVALID_PLAYER |
apply_action, inspect_state, render_game |
The player is not seated in this session. The message lists the seats. |
INVALID_ACTION |
apply_action |
The action is not one an offer gives. Copy one offer's action. |
ACTION_REFUSED |
apply_action |
Boardweaver or the game refused a step, for the reason the message gives. Nothing changed. |
DECISION_IN_PROGRESS |
apply_action, save_checkpoint |
A decision is open: another player's, for apply_action, or any, for save_checkpoint. End it with an offer whose intent is confirm, or drop it with set_state. |
INIT_FAILED, APPLY_FAILED, REFRESH_FAILED |
start_game, apply_action, set_state |
The game logic failed, for example with a panic or a timeout. The message has what the game printed. |
GAME_RUNNER_UNAVAILABLE |
start_game, apply_action, set_state |
Boardweaver cannot run the game right now. Nothing changed. Try again in a minute. |
GAME_RUNNER_BUSY |
start_game, apply_action, set_state |
Boardweaver is running too many other games. Nothing changed. Try again in a moment. |
SESSION_CHANGED |
apply_action, set_state |
Another call changed the session while this one ran, so this call changed nothing. Read the session with inspect_state, then try again. |
LOCALE_NOT_IN_GAME |
start_game, render_game |
locale is not the game's default locale, a locale it has a catalog for, or en-XA. Write /src/i18n/<locale>.json, then start a new session. |
UNSUPPORTED_ON_THIS_VERSION |
start_game, set_state |
validateTypes was passed. A session checks the state against the SDK's state schema, so leave validateTypes out. |
RENDER_UNSUPPORTED |
render_game |
The session has no client to render: it did not build, or the game has none. start_game's warnings says which. |
RENDER_TOO_LARGE |
render_game |
The HTML and CSS are over 100,000 characters together, so the answer holds neither. Render with format: "image". Or the screenshot is over 3,932,160 bytes, so the answer holds no image. Render with a smaller viewport. |
RENDER_FAILED |
render_game |
The client could not start or render. The message says why. |
UNKNOWN_EXAMPLE |
get_example |
Choose a name from the examples listed in the answer. |
UNKNOWN_EXAMPLE_FILE |
get_example |
Call without path for the guide, then copy a file path from its list. |
INVALID_IMAGE |
upload_art, upload_marketing_image |
The image data is incomplete or unreadable. Upload a complete JPEG or PNG file. |
INVALID_UPLOAD_SIZE |
create_upload |
The byte length is over the destination's limit, or a media file is empty. |
UPLOAD_NOT_CONFIGURED |
create_upload |
This Boardweaver environment cannot sign uploads. Pass the file to the upload tool's url or data instead. |
INVALID_UPLOAD_URL, UPLOAD_ALREADY_USED |
signed upload HTTP endpoint | Request a new URL with create_upload. |
UPLOAD_SIZE_MISMATCH, UPLOAD_HASH_MISMATCH |
signed upload HTTP endpoint | Send the exact original bytes, or request a URL for the new file. |
INVALID_UPLOAD_CONTENT_TYPE, INVALID_UPLOAD_TEXT |
signed upload HTTP endpoint | Send raw bytes as application/octet-stream. Text must be valid UTF-8. |
UPLOAD_ABORTED, UPLOAD_TIMEOUT |
signed upload HTTP endpoint | Send the complete file again before the URL expires. |
UPLOAD_METHOD_NOT_ALLOWED |
signed upload HTTP endpoint | Use POST. |
Test limits
Every build runs the unit tests with these limits:
| Limit | Value | When it is hit |
|---|---|---|
| Tests | 200, with names of at most 256 KiB in all | bw::too-many-tests, and no test runs |
| Computation per test | 10,000,000,000 units of fuel, about one per WebAssembly instruction. play_bots keeps its bots to half of it (Testing a bot) |
bw::test-fuel |
| Computation for all the tests | 40,000,000,000 units | bw::test-fuel, and the rest do not finish |
| Memory per test | 50 MiB, more than a match has (Limits in a match), so a passing test does not prove a match fits | bw::test-trap |
| Time | the whole build, tests included, ends 280 seconds after it starts | bw::test-trap, and the rest do not run |
bw::test-fuel names the test that ran out. Every step a test applies, and every bot call, uses fuel. Play fewer decisions or fewer games in that test, or split it into several tests.
Limits in a match
Each call to a game function has these limits:
| Limit | Value | When it is hit |
|---|---|---|
| Memory | 16 MiB | GAME_FAILED |
| Computation | a fixed budget per call, which a long loop or a deep search can use up | EXECUTION_TIMEOUT |
| Time | about 4 seconds per call | EXECUTION_TIMEOUT |
| State | 1 MiB as JSON | GAME_FAILED |
| The offers and changes of one call | 2 MiB as JSON | GAME_FAILED |
| Log lines of one step, or of the opening | 32 lines and 8 KiB as JSON for all of them | GAME_FAILED: table.log panics |
| A message: a log line's, an offer's label or a refusal's reason | a key of 1 to 128 bytes, messages nested at most 8 deep, no value named player, and only seated players |
GAME_FAILED: table.log, the offers or the refusal panics |
A panic in the game logic is GAME_FAILED, with the panic message and any text the game logic printed. Boardweaver records each failure as a match error, which the game's studio lists.
