field-guide / tooling-environment
10/10
10task lane

Tooling & environment

Windows / PowerShell / dotnet traps that corrupt source.

26.07.15a
"[GameResource] and NavMesh.GetSimplePath are obsolete on engine 26.07: use [AssetType] and CalculatePath"

On engine 26.07 both [GameResource] and NavMesh.GetSimplePath are marked [Obsolete] (CS0618), so a zero-warnings build gate fails on them. Replace [GameResource(...)] with [AssetType(...)] and GetSimplePath with NavMesh.CalculatePath, which returns a NavMeshPath of NavMeshPathPoint (not List<Vector3>).

26.08.05
"A custom asset extension can lose a registration race against an engine type"

A project resource type that claims a file extension the engine already owns can lose a registration race, with no error. The losing type's ResourceLibrary.GetAll returns empty and files deserialize as the engine type instead. Which side wins is decided per editor launch. Audit the extension with asset_types before you commit to it.

26.07.15a
"A git worktree can't build: the .csproj files are gitignored"

s&box regenerates .csproj from the editor and gitignores them, so a fresh git worktree's Code/ and Editor/ folders have no project file and dotnet build fails MSB1009. Copy the csproj in, or, at a different depth, author one with absolute paths.

26.07.22
"A green compile status does not prove the new assembly loaded"

The editor can report a clean compile for both the game and editor compilers while the running assembly is still stale. A green status is necessary but not sufficient. Confirm the load with a symbol that is new this session.

26.07.22
"A green offline dotnet build misses s&box whitelist violations"

Compiling an s&box game assembly outside the editor needs the exact csproj shape the editor generates, and even a perfectly green offline build cannot surface whitelist violations. The SB1000 analyzer runs only in the editor compile pass, so code can build with 0 errors and 0 warnings offline and still be rejected in-editor. Build the editor-generated Code/<project>.csproj directly and confirm against the editor compile_status, never the offline build alone.

26.07.22
"A new library Assets/ folder never mounts until you restart the editor"

Code under Code/ hotloads fine, but an Assets/ folder created AFTER the editor started never mounts: asset search finds nothing in it and asset_info reports no asset. It is not a dead watcher; the only fix is restarting the editor.

26.07.22
"A standalone export differs from the editor runtime and breaks editor-built tooling"

A Standalone export changes the data path, log filename, quit behaviour, boot flow, and convar timing versus the editor/published client. Tooling and automation built on editor assumptions silently miss files or spin, verified live on engine 26.07.22.

26.08.05
"An output-path override can poison the engine's base addon and stop it booting"

An s&box game csproj project-references the engine's Base Library csproj inside the Steam install. A first build or a rebuild writes generated intermediates into the engine tree even with an absolute OutputPath, and duplicate attribute files under the base addon then stop the engine booting. BaseIntermediateOutputPath is not the fix, it is a second trap. Clear the base addon's obj and .build folders to recover.

26.07.08e
"Angles struct fields are lowercase"

Angles uses .pitch/.yaw/.roll (lowercase) and Vector3 uses .x/.y/.z. Capitalized sightings in a codebase belong to unrelated component properties.

26.07.22
"Blocking the main thread on a GameTask.RunInThreadAsync result is a permanent deadlock"

Calling .Result or .Wait() on the main thread against a Task from GameTask.RunInThreadAsync deadlocks forever, silently. The process stays alive and the window still responds, but the frame loop is dead. Only join these tasks with await, or compute synchronously on the main thread.

26.08.05
"Build an s&box game assembly offline without stomping the live editor"

A fresh git worktree of an s&box project cannot dotnet build, because the editor generates Code/<project>.csproj and gitignores it, and the csproj references the Steam engine DLLs by relative paths tied to the main tree's directory depth. A naive copy fails with MSB3245 when the worktree nests one level deeper. The durable fix rewrites the csproj with absolute engine paths and redirects OutputPath into an isolated folder. That second half matters everywhere: the default OutputPath is the shared .vs/output/ folder with no project segment, so any CLI build on the machine writes into the same directory. A sibling build there empties a running editor's TypeLibrary, and every game component reads as MissingComponent while compile_status still reports Success. Build headless with the editor closed and output redirected, or pass -p:OutputPath to keep the build out of the shared directory.

26.07.08e
"Character facing derived from velocity flips 180 degrees on every pendulum reversal"

Deriving a visual's facing from horizontal velocity causes 180-degree snaps on any momentum reversal. Lock facing to an attach-time azimuth for pendulums and oscillators.

26.07.15a
"Cross-assembly hot-reload throws MissingMethodException despite green compile"

Adding a public method to the game assembly and calling it from the editor-tools assembly in the same hot-reload pass throws MissingMethodException at runtime: both compile_status and dotnet build are green.

26.07.15a
"Custom GameResource extension must be 8 characters or fewer"

A custom GameResource with a file extension longer than 8 characters silently never registers as a compilable source type. Every asset fails with "no source file." Keep extensions to 8 lowercase chars or fewer.

26.07.08e
"dotnet build verifies compile but NOT the whitelist"

Headless dotnet build is green while the in-editor compiler emits SB1000: Environment/IO/Process/reflection are banned in game code.

26.07.08e
"Editing .razor safely (shell/emoji mojibake)"

Razor HUD emoji and dashes die under Get-Content/Set-Content: edit with byte-safe UTF-8 APIs or a real editor.

26.07.15a
"Editor compiles the checked-out tree, not a git worktree branch"

The editor compiles and verifies its ProjectRoot working tree. A branch that lives only in a git worktree is invisible to it, and compile_status results silently reflect the wrong code.

26.07.08e
"Editor stuck running stale assembly with green compile: only a restart clears it"

compile_status is fully green and the editor accepts edits, but Play executes the OLD code: touch, syntax-error cycling, and play restart all fail. Only a full editor process restart clears it.

26.07.08e
"Engine ships built-in McpTool source as readable C#: read it before writing your own"

The engine ships its built-in [McpTool]s as full C# source, not compiled DLLs: check addons/tools/Code/Mcp/*.cs for canonical patterns before inventing your own.

26.07.08e
"Everything broke at once: check for a stale assembly first"

Failed package compile leaves the editor running the last-good hotload: multi-symptom 'regressions' are often stale code.

26.07.08e
"FacingYawOffset swaps which local axis is the flip axis vs the cartwheel axis"

A front-flip rotation uses _baseRot.Forward (not .Right) when FacingYawOffset is ±90°: the offset swaps which local axis is perpendicular to travel.

26.07.08e
"FileSystem is ambiguous in editor assemblies: CS0104 between Editor.FileSystem and Sandbox.FileSystem"

An unqualified FileSystem compiles in a game assembly but CS0104s in an editor assembly: fully-qualify Sandbox.FileSystem in editor code.

26.07.08e
"Fixing a source asset doesn't always trigger recompilation"

ERROR models or white materials persist after fixing the source: delete the compiled artifact AND touch the source file to force recompilation, or poke asset_compile per source file.

26.07.15a
"Git worktree at different depth breaks sbox csproj relative references"

A git worktree at a different directory depth than the main checkout breaks the generated csproj's relative references to the sbox install: dotnet build fails with missing-assembly errors.

26.07.08e
"Green compile_status coexists with a stale play-mode hotload reporting phantom errors"

compile_status shows Success and dotnet build is green, but play mode reports errors for symbols and lines that no longer exist in source: the play-mode hotloader compiled a stale mid-edit snapshot.

26.07.15a
"Hand-vendored library without an .sbproj is silently skipped"

The editor discovers a library under Libraries/ only if the folder contains exactly one .sbproj file. A hand-vendored library without one is silently ignored, producing a wall of CS0246 errors on every type it defines.

26.07.08e
"Headless dotnet build misses Razor compile errors"

dotnet build reports 0 errors on .razor files the in-editor compiler rejects: the headless build doesn't surface the Razor errors the live editor's Roslyn compiler flags.

26.07.15a
"Headless dotnet build skips sandbox whitelist enforcement"

A headless dotnet build against the engine assemblies does not enforce the s&box sandbox access-control whitelist: code that builds clean headlessly can fail the editor compile with SB1000.

26.07.18
"Library extraction seam must cover every side effect"

A library extraction seam covers only the headline behavior: every other side effect of the replaced code path silently disappears unless explicitly reproduced on the consumer side.

26.07.15a
"Library host project fails to compile library-mounted scenes"

A library host project fails to compile a library-mounted .scene asset: the resource compiler probes a mangled path, and the scene silently becomes an empty impostor.

26.07.22
"Model.Load of a missing vmdl returns the error model, not null"

Model.Load of a missing vmdl can return the orange ERROR mesh with the requested path as its Name: check IsError, not just null. And a failed load is cached for the life of the editor PROCESS: play_stop/play_start won't clear it, only a fresh editor will.

26.07.08e
"Moving legacy assets with git mv leaves compiled files behind and misses shared textures"

Moving legacy assets out of a publish payload with git mv alone leaves compiled artifacts behind (they are gitignored), and a material/model-only dependency closure misses shared textures that kept assets still reference.

26.07.18
"Offline two-assembly compile gate with prebuilt base library"

A consumer game plus a vendored library can be compile-gated fully offline using two scratch csprojs against the editor install's prebuilt base library DLL.

26.07.08e
"s&box ships a headless dedicated server that can run an unpublished local project"

App 1892930 via SteamCMD runs a headless server with +game pointing at a local .sbproj: clients stream code/assets, no sbox.game publish required.

26.07.22
"Setting a [ConVar] to the value it already holds does nothing"

Re-setting a [ConVar] to the value it already holds is a silent no-op: the C# setter never runs, so a convar-triggered action does nothing, with zero console output. Statics survive Play stop/start, so a prior session leaves the value already set.

26.07.08e
"Stalled Steam update half-deletes the s&box install"

If sbox-launcher.exe stays open during a Steam update, files vanish mid-install. Validate via steam://validate/590830.

26.07.22
"Static registry persists across editor Play restarts: gate on live objects"

A C# static list/registry survives editor Play stop/start and code hotloads, holding references to destroyed GameObjects: gate iteration on IsValid(), don't rely on clearing alone. It can also mask a new scene's state when stale high-priority entries outrank it. Hand registrations back in OnDestroy.

26.07.08e
"Static registry populated by a static constructor doesn't pick up new entries on hotload"

s&box carries old static state forward on hotload and never re-runs static constructors: a registry entry added in source is absent at runtime despite green compile.

26.07.08e
"Structural Razor edit hotload exception silently deregisters project tools"

A structural .razor change can throw a hotload exception that deregisters the project's entire toolset while compile_status still reports Success. Restart the editor to recover.

26.07.15a
"The editor MCP port is one global setting: bump it by hand, let your agent discover it"

The editor MCP port is one engine-global preference, not a per-project setting. When the configured port is already bound by another open editor, the second editor logs a bind failure and starts no MCP server at all; it does not auto-increment to a free port. Bump the port by hand before (or after) launching the second editor, and have agents discover the live port by scanning and matching the Project field instead of pinning a number.

26.07.15a
"The official s&box docs are machine-readable via llms.txt and .md URLs"

The official s&box docs serve raw markdown by appending .md to any doc URL, and expose a full index at sbox.game/llms.txt. The site itself is a client-rendered Blazor app whose HTML contains no content, so scraping gets nothing.

26.07.22
"The repeating "error texture" / default_mask console flood is base-menu content, not your project"

A repeating red "Texture manager doesn't know about texture ... default_mask ... returning error texture" pair on engine/RenderSystem is base-menu-addon noise mounted into every project, not your content. It repeats per draw call. There is no supported way to mute it from game code. Filter it visually.

26.07.15a
"The stock PlayerController has no public speed property: you can't scale move speed from outside"

Sandbox.PlayerController's WalkSpeed / RunSpeed / DuckedSpeed / Speed are private serialized [Property] fields, so an external component can't cleanly scale player move speed (e.g. for a slow/root effect). WishVelocity is public but recomputed every fixed-update, so writing it is order-dependent. Swap in or subclass a controller that exposes a public knob.

26.07.15a
"There is no scene/global time-scale API: slow-mo has no seam to set"

The game-reachable engine surface has no scene/global time-scale multiplier: Scene.TimeScale does not exist, and the only TimeScale anywhere is on ParticleEffect. Sandbox.Time is read-only. A slow-mo / fast-forward feature must scale each system's own Time.Delta reads, or expose a hook for a future native mechanism.

26.07.08e
"Windows SSH-launched server process dies when the SSH session closes"

A long-running process started over a Windows OpenSSH session is killed the moment the session closes -- use a Scheduled Task instead.

26.07.08e
"Windows/PowerShell traps that corrupt s&box source"

Get-Content/Set-Content re-encodes BOM-less UTF-8 as ANSI; CRLF files break \n-only search-replace: use byte-safe APIs.

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord