Troubleshooting

Start with the editor's Output panel and the exact returned error. Keep your existing scene and assets while investigating; a restart or an empty new project is not a substitute for finding the cause.

The editor does not start

Separate a build failure from an editor startup error. The launch script first builds TTREditor and TTRForgeWorker; a compiler error means that launch has not happened yet. The package targets macOS 14 or later. If engine creation fails after launch, the editor shows “TTR Engine can't start” with the underlying error. Report that text rather than only saying the window is missing.

Do not assume a Windows/Linux runtime build supplies the AppKit editor. This guide's launch route is macOS.

The wrong scene opened

Check the project folder and scene name. With no explicit project, startup searches for Projects/Sandbox; with no explicit scene, it uses Downtown. Missing scenes can lead to a starter scene, and an opening error can lead to a fallback demo. Use File → Open Scene… and check Output to confirm the file you intended actually opened.

Switching between Make and Pro mode

Press ⌘K and choose Make Mode or Pro Mode; your AI can switch them too (editor_command). Both are the same editor with the same commands. See the first five minutes.

An agent cannot connect

Set the app up with Help › Connect an AI App… (Windows and Linux: Connect your AI on the welcome screen); see Connect your AI. "Pairing required" means the app has no token or an old one: run the setup for it again. Earlier versions named the server ttr-engine; if your app lists both, remove the old one (Claude Code: claude mcp remove ttr-engine). Check the Agent panel for listening/error state and the actual port. The default endpoint is http://127.0.0.1:7357/mcp; startup settings can change the port or disable the link. The client must reach that same Mac's loopback interface.

The endpoint expects POST with a JSON-RPC body. Opening it as a normal browser page sends GET, which returns 405; that does not prove the service is broken. Initialize, then request tools/list. A tool absent from that returned catalogue is not available in the running version, even if newer source documentation lists it.

If the port is occupied, identify the existing editor/link rather than killing an unrelated process or launching duplicate listeners. Do not publish the local port as an internet endpoint.

A tool returned an error—or a report containing FAIL

Check whether the failure is a JSON-RPC error or result.isError. Check the arguments against the returned schema, including required fields, vector lengths and allowed choices. Some operations require combinations such as target-or-mesh in addition to fields marked required.

Read the report body too. A tool can successfully produce a report that contains failed, pending, unchecked or unexercised checks. That means the reporting action worked, not that the game passed. Resolve an open rehearsal before operations that are refused because they replace the scene or write shared assets. Do not disable that protection merely to make the call return success.

A model is missing or has no textures

Check the mesh path relative to the project's Assets/ folder. For .gltf, keep the external buffers and texture files with the model during import. Read the asset/import error in Output. Use distinct imported .glb filenames when keeping two versions, because importing the same name can replace the existing imported file.

Generator and reference examples need real local asset kits, recipes or reference images. A sample pathname in the guide is not a supplied asset.

My script does not run, or still runs the old code

Check that the script component names a file relative to the project's Scripts/ folder, that the file exists, and that the callback names are spelled exactly. Read the reported filename and line number for syntax/runtime errors. Stop and start Play after editing a file; writing new bytes does not replace existing running handlers.

world.find can return null. Check it before accessing a property. State stored as an arbitrary property of a callback's self wrapper is not persistent; use a closure variable, self.params, or self.save for their intended purposes.

“Took longer than 50 ms” or out of memory

Look for endless loops, recursion or large per-tick work. Break a long operation into bounded steps over several ticks. Nested JavaScript calls share the outer CPU-time budget. Due timers also share a guarded timer pass, so scheduling many expensive callbacks for the same tick does not make them free.

The normal guard is 50 ms of JavaScript CPU time, with a longer startup budget. After three attributed timeouts, the offending file/entity instance is disabled for the rest of Play. Fix the cause before starting a new session. The default 256 MiB memory cap is shared by the VM; remove retained arrays/objects rather than assuming each object gets its own allowance.

Objects disappear, the camera won't drag, or the editor grows large

Ask the editor before guessing. Three agent tools describe the live viewport:

  • viewport_report lists every model and why it is or isn't drawn right now: in view, outside the camera's view, hidden by see-through walls, still loading, or missing, with its level of detail and the camera's orbit point. {"hidden_only": true} lists only what should show but doesn't; {"entity": "Coffee_Table"} explains one object. screenshot {"live": true} takes the picture exactly as the viewport shows it (with the cutaway applied).
  • viewport_input plays a gesture through the viewport's own mouse handlers, frame by frame, without moving the real pointer: {"gesture": "drag", "from": [0.5, 0.5], "by": [200, 0]} (also orbit, middle_drag, scroll, click). It reports which view a real click there lands on (a panel or a modal window over the viewport shows up here), how far the camera moved and how many points the grabbed spot drifted from under the pointer. agent_changes keeps an agent editing an object during the gesture, to check that the person's drag still wins. It plays camera gestures only: it refuses any gesture during Play, a drag that starts on the move gizmo, and drag, orbit or click while Sculpt Mode or a paint brush is on (a left press would sculpt or paint); middle_drag and scroll still work there.
  • memory_report gives the process footprint, the GPU total and GPU memory by renderer part, plus the largest models. It reads the renderer between frames, so the live view waits for it once (tens of milliseconds on a big scene).

See-through walls (View menu) leaves out only architecture between the camera and the point it turns around: objects at least 1.5 m tall and 1.5 m long (walls, partitions, roofs, whole buildings, big crowns) and ceilings above that point. Smaller furniture and props are never cut away, and neither is the selection. Size alone can't tell a wardrobe, a tall bookshelf, an SUV, a statue or a tree from a wall, so an object whose name or model file names a big prop (wardrobe, bookshelf, cabinet, fridge, sofa, bed, car, SUV, van, truck, bus, statue, tree, crate and the like; the folder counts too, as in Vehicles/Sedan.glb) is kept drawn. A big prop with a plain name (Box_07) is still cut away like a wall: tag it. Tag an object about { tags ["wall"] } to let a low wall (or a building with a prop-like name, such as Bus_Station) go, or ["no_cutaway"] (or prop, furniture) to keep a big one drawn; tags decide before names. viewport_report shows the cutaway's cost on the last frame.

Memory follows the open scene: opening another scene lets go of the models and material surfaces only the last scene used (opening it again streams them back; sculpted shapes come from their disk cache). Built-in shapes and generated meshes stay; a rigged character goes with its animation data and hair. The log says how many were let go. A model or surface the last scene was still streaming in when you left it is dropped when it lands ("Dropped ... after its scene was closed"), unless the new scene uses it too. A slow window at launch is logged too (Window shown N ms after launch); the Agent Wall's lane pictures decode in the background and never hold it up.

Movement goes through walls, or a path is false

self.move and assigning self.position directly are not collision-aware walking. Use self.walk with a displacement, and provide the collision/body setup the movement bridge expects. Multiply meters-per-second speed by dt. Visual scale changes do not resize an existing collision shape.

self.pathTo has three different outcomes: a route array, null for no route, or false when the session's three-search-per-tick quota has been used. Wait until a later tick after false. A tight retry loop spends time without restoring the quota.

lookAt(null) fails, or raycast has no limb

These are current binding details, not missing documentation options. Entity.lookAt is declared twice; the later yaw-setting implementation overrides the earlier head/eye helper and does not accept null. Use a real target, or face for player heading changes.

The public world.raycast result includes entity, point, normal and distance. It currently drops the native hit's limb field. Do not build script logic around a property the wrapper does not return.

Sound or scene changes do nothing in headless tests

Headless play does not play audible sound. The native sound return in that mode recognizes built-in names; it is not a playback receipt. In the current implementation, game.loadScene returns early for hidden sessions, so a headless run does not prove a live scene transition. Verify those routes in the appropriate live player/editor workflow separately.

Saving or loading returns false

Use slot names of 1–40 letters, digits, underscores or hyphens. Read Output for a write error or a compatibility rejection. Saves belong to the scene/session context: renaming or removing required objects, or loading a save from another scene path, can invalidate it. game.hasSave checks load compatibility, not only file existence.

Keep plain data in self.save. Arbitrary closure variables and function objects are not your save format. An accepted load during a script callback is applied at a safe point, rather than halfway through that callback.

Share a useful bug report

Include the source/build version, the smallest scene and script that reproduce the issue, the exact steps or tool request, and the actual error/report. Include a screenshot when the failure is visual. Distinguish “file written,” “test passed,” and “worked in live Play.” Remove private paths or unrelated data before sharing logs publicly.

Arcwright Beta 2 (0.4.0) · generated from the engine source (25e375c86)Something wrong or missing? Tell us
Download Arcwright free