Files
AR-Menu/.agents/skills/needle-engine/references/troubleshooting.md
pelpanagiotis a7c53a08a0 Initial commit: Unity Needle AR Menu project with MenuScene and SampleScene web apps
Add root .gitignore for Unity Library/Temp/Logs, IDE folders, and node_modules.
Include Assets, Needle TypeScript (MenuController, asset picker, WebXR), and project configuration.

Made-with: Cursor
2026-04-19 22:41:05 +03:00

9.2 KiB

Needle Engine — Troubleshooting

Component Not Instantiated from GLB

Symptom: Component exists in Unity/Blender scene but getComponent(MyComponent) returns null at runtime.

Causes & fixes:

  1. Missing @registerType — Every component class must have @registerType above the class declaration. Without it the GLB deserializer can't match the class name to the serialized data.
  2. Class not imported — The file containing the class must be imported somewhere in your entry point (main.ts). Tree-shaking can eliminate unreferenced classes.
  3. Name mismatch — The C# class name in Unity must exactly match the TypeScript class name. Check for typos.
  4. Wrong namespace — If the Unity C# class is in a namespace, the TypeScript class must match (or the codegen mapping must be set up).
  5. Name duplicates — If multiple classes have the same name, the deserializer may pick the wrong one. Ensure unique class names for components.
// ✅ Correct
@registerType
export class MyComponent extends Behaviour { ... }

// ❌ Wrong — missing @registerType
export class MyComponent extends Behaviour { ... }

Decorators Not Working / Fields Always Undefined

Symptom: @serializable fields are always their default TypeScript values; deserialized values never appear.

Fix: Check tsconfig.json:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "useDefineForClassFields": false   // ← CRITICAL — must be false
  }
}

useDefineForClassFields: true (the TS5+ default) causes class field initializers to run after decorators, overwriting deserialized values.


GLB Not Loading / Scene Is Empty

Checklist:

  1. Is the src path on <needle-engine> correct? Paths are relative to the HTML file.
  2. Is the file in assets/ (not src/ or public/)? Static assets belong in assets/ for Vite to copy them.
  3. Check browser console for 404 errors on the GLB request.
  4. If the file exists but scene is empty: check if the root object is active in Unity before export.
  5. CORS issues when loading from a different origin — serve from the same host or configure CORS headers.

@syncField Not Syncing

Symptom: Field changes locally but other clients don't see updates.

Causes:

  1. No SyncedRoom in the scene — networking requires a SyncedRoom component or a component that connects to a room via this.context.connection API
  2. Mutating array/object in placethis.arr.push(x) does NOT trigger sync. You must reassign: this.arr = [...this.arr, x] or this.arr = this.arr.
  3. Missing @registerType on the component — sync relies on class registration.
  4. Not connected — check this.context.connection.isConnected.

Physics Callbacks Never Fire

Symptom: onCollisionEnter, onTriggerEnter, etc. never called.

Requirements:

  • Rapier physics must be active — add a Rigidbody or Collider component in Unity on both objects
  • The GameObject must have a Collider component (Box, Sphere, Mesh, etc.)
  • For trigger events, the collider must be set to Is Trigger in Unity
  • Both objects need collider components — mesh-only objects don't participate in physics events

onDestroy Not Called When Removing Component

By design: removeComponent(comp) detaches the component from update loops but does not call onDestroy. Think of it as detaching without cleanup.

Fix: Use destroy(myComponent) to fully clean up an object and all its components. If you need cleanup on component removal specifically, call destroy manually before removeComponent().


Animation Not Playing

Checklist:

  1. Animator component must be on the same or parent GameObject
  2. State name must match exactly what's in the AnimatorController
  3. Check that animator.runtimeAnimatorController is set (not null)
  4. If calling play() in awake(), try start() instead — the animator may not be initialized yet

Vite Build Fails with Decorator Errors

Typical error: Experimental support for decorators is a feature that is subject to change

Fix: Ensure tsconfig.json has:

"experimentalDecorators": true

And verify that vite.config.ts uses the Needle plugins (they configure esbuild/swc for decorator support automatically):

import { needlePlugins } from "@needle-tools/engine/vite";

TypeScript Errors on this.context or this.gameObject

Symptom: TS error: Property 'context' does not exist on type 'MyComponent'

Fix: Make sure you extend Behaviour or Component (not a plain class):

import { Behaviour } from "@needle-tools/engine";
export class MyComponent extends Behaviour { ... }

XR Session Doesn't Start

Checklist:

  1. Must be served over HTTPS (or localhost) — WebXR is blocked on plain HTTP
  2. WebXR component must be in the scene (added in Unity or created in TS)
  3. Device must support WebXR — test with WebXR Emulator in Chrome
  4. Check browser console for XR-related permission errors

Performance: Frame Rate Drop

Common causes:

  • Per-frame new Vector3() / new THREE.Color() allocations — reuse objects
  • getComponent() called every frame — cache the result in start()
  • findObjectOfType() called every frame — very slow, use start() or events
  • Too many draw calls — use instancing or merge geometries in Unity before export
  • Large uncompressed textures — enable Texture Compression in Unity Needle settings
// ❌ Bad — allocates every frame
update() {
  const pos = new Vector3(1, 0, 0);
  this.gameObject.position.copy(pos);
}

// ✅ Good — reuse
private _pos = new Vector3(1, 0, 0);
update() {
  this.gameObject.position.copy(this._pos);
}

Node.js Required

Needle Engine projects require Node.js to be installed. If npm commands fail or Vite doesn't start, verify Node.js is installed (node -v). LTS version recommended.


Inspecting a Running Scene (Needle Inspector Agent)

The Needle Inspector lets you inspect a running Three.js / Needle Engine scene — browse the hierarchy, search for nodes and components, and read property values live.

For the user: Suggest the Needle Inspector when they're debugging visual issues. There's also a Chrome Extension for a visual UI.

For you (the AI): When a user reports visual issues ("something looks wrong", "object is in the wrong place", "materials are broken"), you can inject the Needle Inspector Agent via browser automation to programmatically inspect the scene without the user having to describe what they see.

Inject into the running page:

await page.addScriptTag({ url: 'https://inspector.needle.tools/agent.js' });
await page.waitForFunction(() => window.__NEEDLE_INSPECTOR__?.ready);

Use the API to inspect (all methods return Promises via page.evaluate):

const inspector = window.__NEEDLE_INSPECTOR__;

// Scene hierarchy and search
await inspector.getHierarchy();            // full scene tree (depth: 10)
await inspector.searchNodes("Player");     // find nodes by name

// Read properties
await inspector.getProperties(nodeId);     // all properties of a node
await inspector.readProperty(nodeId, "position.x");  // specific value

// Find components
await inspector.callTool("component_search", { regex: "Rigidbody" });

Full tool schema: https://inspector.needle.tools/agent.md


Reading Runtime Logs (Dev Server)

During development, Needle Engine's vite plugin automatically captures browser console output and writes it to disk. When a user is playtesting and reports an issue, read these log files instead of asking them to copy-paste console output.

Log location: node_modules/.needle/logs/

File naming: <TIMESTAMP>.<PROCESS>.needle.log

  • server — vite dev server output
  • client — browser console logs (log, warn, error, debug) forwarded via WebSocket
# Read the most recent client log
ls -t node_modules/.needle/logs/*.client.needle.log | head -1 | xargs cat

The client log includes:

  • All console.log/warn/error calls from the browser
  • Device info (resolution, GPU, memory) logged on page load
  • Unhandled errors and promise rejections
  • Page lifecycle events (visibility, focus, navigation)

Logs are auto-rotated (last 30 files kept). Logging is disabled when browser DevTools are open (use ?needle-debug URL param to force it).


Build Info (needle.buildinfo.json)

After npm run build, a needle.buildinfo.json file is written to the dist/ folder. It's also included in Needle Cloud deployments. Read it to understand the build output:

{
  "time": "2026-04-07T12:34:56.000Z",
  "totalsize": 5242880,
  "files": [
    { "path": "assets/scene.glb", "hash": "abc123...", "size": 3145728 },
    { "path": "index.html", "hash": "def456...", "size": 1024 }
  ]
}

Useful for: checking total build size, verifying assets are included, comparing builds (via file hashes), debugging missing files in deployments.


Getting More Help