> ## Documentation Index
> Fetch the complete documentation index at: https://hyperscape-ai-mintlify-docs-update.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting

> Common issues and solutions

## Common Issues

### WebGPU Not Supported Error

**Symptom**: Error screen showing "WebGPU not supported. Please upgrade your browser."

**Cause**: Browser doesn't support WebGPU (required as of February 2026)

**Solution**: Upgrade to a WebGPU-compatible browser:

**Supported Browsers:**

* Chrome 113+ (recommended)
* Edge 113+
* Safari 18+ (macOS Sonoma or later)
* Firefox Nightly (experimental)

**Why WebGPU is Required:**

* All shaders use Three.js Shading Language (TSL)
* TSL only compiles to WGSL (WebGPU Shading Language)
* No WebGL fallback possible without rewriting all shaders
* Procedural grass, terrain, particles all require WebGPU

**Check WebGPU Support:**

```javascript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// In browser console
if (navigator.gpu) {
  console.log('WebGPU supported!');
} else {
  console.log('WebGPU not supported - upgrade browser');
}
```

**Technical Details:**

* Commit: 3bc59db (February 26, 2026)
* File: `packages/shared/src/systems/client/ClientGraphics.ts`
* User-friendly error screen shows browser upgrade instructions

### Characters Vanishing

**Symptom**: Characters disappear after page refresh

**Cause**: Missing Privy credentials—each refresh creates a new anonymous identity

**Solution**: Configure Privy authentication:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# packages/client/.env
PUBLIC_PRIVY_APP_ID=your-app-id

# packages/server/.env
PUBLIC_PRIVY_APP_ID=your-app-id
PRIVY_APP_SECRET=your-app-secret
```

### Assets Not Loading

**Symptom**: 404 errors for models/avatars

**Cause**: CDN container not running

**Solution**:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
bun run cdn:up
```

Or ensure you're using `bun run dev` which starts it automatically.

### Database Schema Errors

**Symptom**: Errors about missing columns or tables

**Cause**: Schema changed but migrations not run

**Solution**:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cd packages/server
bunx drizzle-kit push
```

For a complete reset (destroys data):

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
docker stop hyperscape-postgres
docker rm hyperscape-postgres
docker volume rm hyperscape-postgres-data
docker volume rm server_postgres-data
bun run dev
```

### Manifest Validation Errors

**Symptom**: DataManager validation errors about invalid JSON in manifest files

**Cause**: Invalid JSON syntax in manifest files (trailing commas, missing quotes, etc.)

**Solution**: Recent commits fixed several manifest JSON errors. Update to latest code:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
git pull origin main
bun install
bun run build
```

If you're editing manifests manually, validate JSON syntax:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Validate a manifest file
cat packages/server/world/assets/manifests/npcs.json | jq .
```

**Common Issues:**

* Trailing commas in JSON arrays/objects
* Missing quotes around property names
* Incorrect .gitignore negation for manifest files

**Recent Fixes:**

* Fixed invalid manifest JSONs causing DataManager validation errors (commit aa1ea2d)
* Corrected .gitignore manifest negation to properly track manifest files (commit 1f4551c)
* All manifest files now tracked in git for CI test data availability (commit 2dddf28)

### Port Conflicts

**Symptom**: "Port already in use" errors

**Solution**: Kill processes on conflicting ports:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
lsof -ti:3333 | xargs kill -9   # Client
lsof -ti:5555 | xargs kill -9   # Server
lsof -ti:8080 | xargs kill -9   # CDN
lsof -ti:4001 | xargs kill -9   # ElizaOS
```

### Build Errors

**Symptom**: Compilation fails

**Solution**: Clean rebuild:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
bun run clean
rm -rf node_modules packages/*/node_modules
bun install
bun run build
```

### ESLint Crashes with ajv TypeError

**Symptom**: ESLint crashes with `TypeError: Class extends value undefined is not a constructor or null` related to ajv

**Cause**: Forcing ajv\@8 on @eslint/eslintrc which requires ajv\@6 for Draft-04 schema support

**Solution**: Update to latest code (commit b344d9e fixed this):

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
git pull origin main
bun install
```

**Technical Details:**

* Removed `ajv>=8.18.0` override from root package.json
* @eslint/eslintrc needs ajv\@6 for JSON Schema Draft-04 support
* Forcing ajv\@8 caused TypeError crash in ESLint initialization
* Security fix for ajv (GHSA-2g4f-4pwh-qvx6) applied only to packages that support ajv\@8

### Drizzle Migration Failures in CI

**Symptom**: Server migration code fails with "table already exists" errors in CI, or migration 0050 fails with "relation already exists" (42P07) on fresh databases

**Cause**: Multiple issues with migration system:

1. Running `drizzle-kit push` separately creates tables without populating migration journal
2. Migration 0050 duplicated CREATE TABLE statements from earlier migrations without IF NOT EXISTS guards
3. `SKIP_MIGRATIONS=true` didn't skip table validation, causing failures when schema created externally

**Solution**: Update to latest code (commits eb8652a, 6a5f4ee, e4b6489 fixed this):

**For CI/Integration Tests:**

```yaml theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# ✅ Use drizzle-kit push + SKIP_MIGRATIONS for declarative schema
- name: Create database schema
  run: bunx drizzle-kit push
  
- name: Run tests
  run: bun test:integration
  env:
    SKIP_MIGRATIONS: true  # Skip ALL migration checks
```

**For Production:**

```yaml theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# ✅ Let server handle migrations normally
- run: bun run start
# No SKIP_MIGRATIONS, no drizzle-kit push
```

**Technical Details:**

* **Commit eb8652a**: CI now uses `drizzle-kit push` for declarative schema creation
* **Commit 6a5f4ee**: `SKIP_MIGRATIONS=true` now skips table validation AND migration recovery loop
* **Commit e4b6489**: Migration 0050 fixed with IF NOT EXISTS guards for idempotency
* Server's built-in migrations have FK ordering issues (migration 0050 references `arena_rounds` from older migrations)
* `drizzle-kit push` creates schema declaratively without these problems
* `SKIP_MIGRATIONS=true` tells server to skip ALL migration-related checks when schema is created externally

**Migration 0050 Fix Details:**

Migration 0050 previously duplicated CREATE TABLE statements from earlier migrations:

* `agent_duel_stats` (from migration 0039)
* `arena_fee_shares`
* `arena_staking_points`
* `wallet_links`

On fresh databases, running all migrations sequentially caused 42P07 errors when the same table was created twice. Now all CREATE TABLE and CREATE INDEX statements use IF NOT EXISTS guards.

### Circular Dependency Build Errors

**Symptom**: TypeScript errors about missing types from `@hyperscape/shared` or `@hyperscape/procgen`

**Cause**: Circular dependencies between packages during clean builds

**Solution**: The build system handles this automatically with resilient patterns:

* **procgen ↔ shared**: Uses `tsc || echo` to exit 0 even with circular dep errors
* **plugin-hyperscape ↔ shared**: Uses `--skipLibCheck` in declaration generation
* Packages produce partial output sufficient for downstream consumers

If you still see errors:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Build shared first to create dist/ directory
cd packages/shared
bun run build

# Then build dependent packages
cd ../procgen
bun run build

cd ../plugin-hyperscape
bun run build
```

### PhysX Build Fails

**Symptom**: Errors during PhysX compilation

**Cause**: PhysX WASM build requires emscripten

**Solution**: PhysX is pre-built and committed. If you must rebuild:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cd packages/physx-js-webidl
./make.sh   # Requires emscripten toolchain
```

### Tests Failing

**Symptom**: Tests timeout or fail unexpectedly

**Causes**:

* Server running during tests
* Stale test data
* Port conflicts
* CI environment variance (slower than local)
* Missing WebGPU mocks for Three.js WebGPU renderer

**Solution**:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Stop any running servers
lsof -ti:5555 | xargs kill -9

# Check logs
ls /logs/

# Run tests
npm test
```

**CI-Specific Issues:**

Recent commits have adjusted test timeouts for CI reliability:

* **Geometry validation**: Increased to 120s for intensive procgen tests
* **Benchmark thresholds**: Relaxed for NPCTickProcessor to account for CI variance
* **Vitest timeout**: Global timeout increased to prevent flaky failures
* **Performance tests**: Adjusted heap growth and scaling thresholds for parallel test contention

If tests pass locally but fail in CI, check if timeouts need adjustment for your specific test.

### Integration Tests Fail with "anvil: command not found"

**Symptom**: Integration tests fail because the anvil binary is missing

**Cause**: Foundry toolchain not installed in CI environment

**Solution**: Update to latest code (commit b344d9e fixed this):

```yaml theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# .github/workflows/integration.yml now includes:
- name: Install Foundry
  uses: foundry-rs/foundry-toolchain@v1
```

**For local development**, install Foundry:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
curl -L https://foundry.paradigm.xyz | bash
foundryup
```

### Test Suite Fails with WebGPU Errors

**Symptom**: Tests fail with "GPUShaderStage is not defined" or similar WebGPU errors

**Cause**: Three.js WebGPU renderer expects browser globals that don't exist in test environment

**Solution**: Update to latest code (commit 25ba63c fixed this):

**Technical Details:**

* Added `vitest.setup.ts` to mock WebGPU browser globals
* Mocks: GPUShaderStage, GPUBufferUsage, GPUTextureUsage, etc.
* Required by Three.js WebGPU renderer in tests
* Fixed in `packages/server/vitest.setup.ts`

### ArenaService Tests Fail After Refactor

**Symptom**: ArenaService tests fail with "Cannot spy on private method" errors

**Cause**: Refactored ArenaService architecture moved methods to sub-services

**Solution**: Update to latest code (commit 25ba63c fixed this):

**Technical Details:**

* Added protected passthrough methods on ArenaService for test spying
* Methods: getDb, getEligibleAgents, findReferralMappingForWalletNetwork, etc.
* Database mock helper: `setDbMock` properly configures world.getSystem("database") mock
* Some tests skipped pending deeper refactoring (85 skipped, 1569 passing)

### WebSocket Connection Failed

**Symptom**: Can't connect to game server

**Causes**:

* Server not running
* Wrong port configuration
* Firewall blocking

**Solution**:

1. Verify server is running: `lsof -i:5555`
2. Check client config: `PUBLIC_WS_URL`
3. Ensure no firewall blocks

### Hot Reload Not Working

**Symptom**: Changes don't appear in browser

**Causes**:

* Build cache stale
* Wrong directory

**Solution**:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
bun run dev:reset
```

### Database Pool Starvation

**Symptom**: "200 pending operations" warnings, game freezes during batch operations (fletching, smithing)

**Cause**: Concurrent inventory writes exhausting database connection pool

**Solution**: Update to latest code (PR #823 fixed this). The system now uses write coalescing:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// Collapses N concurrent writes into ≤2 DB transactions per player
// Before: 100 fletching completions = 200 sequential transactions
// After: 100 fletching completions = 2 transactions per player
```

**Technical Details:**

* Write coalescing keeps only the latest inventory snapshot
* At most 2 transactions per player: one active + one queued
* All waiting callers resolve when batch completes
* Prevents PostgreSQL deadlocks and pool exhaustion

**Verification:**
Check `packages/server/src/systems/DatabaseSystem/index.ts` for `inventoryWriteActive` and `inventoryWriteQueued` maps.

### Camera Facing Backwards on Fresh Load

**Symptom**: Camera initializes in front of player, causing backwards movement controls

**Cause**: Camera spherical coordinates initialized with `theta=0` instead of `theta=Math.PI`

**Solution**: Update to latest code (PR #829 fixed this)

**Technical Details:**

* Camera uses spherical coordinates (radius, phi, theta)
* `theta=0` places camera in front of player (incorrect)
* `theta=Math.PI` places camera behind player (correct third-person view)
* Fixed in `ClientCameraSystem.ts` initialization and reset methods

### Entity Outlines Showing Wrong Colors

**Symptom**: Entity highlights show incorrect color grading when hovering, even with post-processing disabled

**Cause**: Post-processing outline pass shares shader pipeline with LUT color grading, causing stale LUT data to leak into outline-only rendering

**Solution**: Update to latest code (PR #829 fixed this)

**Technical Details:**

* LUT intensity now zeroed when color grading is disabled
* Rendering routes through composer even when post-processing is off
* Ensures entity highlights work correctly without color grading artifacts
* Fixed in `ClientGraphics.ts` and `PostProcessingFactory.ts`

### Remote Players Facing Sideways

**Symptom**: Other players appear to face sideways or show T-pose ghosts in multiplayer

**Cause**: Missing quaternion sync and transform updates after async avatar loading

**Solution**: Update to latest code (PR #875 fixed this)

**Technical Details:**

* `base.quaternion` now synced in `PlayerRemote` for correct facing direction
* Force transform update after async avatar load prevents T-pose ghosts
* Spatial index updated after teleport/respawn so `sendToNearby` reaches players at new location
* Authoritative position broadcast to all players on join
* Fixed in `PlayerRemote.ts` and `ServerNetwork/index.ts`

### Equipment Not Visible on Other Players

**Symptom**: Equipment doesn't appear on remote players, or only shows after reconnect

**Cause**: Equipment packets sent before VRM avatar loaded, or lost during disconnect

**Solution**: Update to latest code (PR #875 fixed this)

**Technical Details:**

* Subscribe to `AVATAR_LOAD_COMPLETE` to replay cached equipment when VRM loads
* Re-send equipment on socket reconnect (recovers packets lost during disconnect)
* Send existing players' equipment to joiner and broadcast joiner's equipment
* Use all 11 `EQUIPMENT_SLOT_NAMES` instead of hardcoded 6 slots
* Fixed in `EquipmentVisualSystem.ts` and `ServerNetwork/index.ts`

### Duel Loser Stuck in Arena

**Symptom**: After duel ends, loser is stuck in arena with frozen physics and cannot move or respawn

**Cause**: Winner's health restore exception prevented loser's health restore from executing

**Solution**: Update to latest code (PR #875 fixed this)

**Technical Details:**

* Split health restores into individual try/catch blocks
* Previously both `restorePlayerHealth` calls were in single try/catch
* If winner's restore triggered exception in any `PLAYER_RESPAWNED` handler, loser's restore was skipped
* Left loser with frozen physics, `isDying=true`, and no `playerRespawned`/`playerSetDead` packets
* Each restore now individually wrapped, matching teleport pattern
* Fixed in `DuelCombatResolver.ts`

### PvP Ranged Attacks Granting Wrong XP

**Symptom**: Killing players with ranged attacks in duels grants Strength XP instead of Ranged XP

**Cause**: XP calculation used player's melee attack style instead of actual weapon type

**Solution**: Update to latest code (PR #875 fixed this)

**Technical Details:**

* `PlayerDeathSystem` now detects weapon type (bow/crossbow/staff) for PvP kills
* Previously used player's melee attack style, incorrectly granting Strength XP
* Now matches `MobEntity` logic: inspect actual weapon type for XP calculation
* Fixed in `PlayerDeathSystem.ts`

## Logs

### Server Logs

Check terminal output where `bun run dev` is running.

### Test Logs

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ls packages/server/logs/
```

### Docker Logs

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
docker logs hyperscape-postgres
docker logs hyperscape-cdn
```

### Database Prepared Statements Error (XX000)

**Symptom**: Database queries fail with error code `XX000` when using Supabase Supavisor pooler

**Cause**: Supavisor connection pooler doesn't support prepared statements in transaction mode

**Solution**: Update to latest code (commits 8aaaf28, f7ab9f7 fixed this). Prepared statements are now disabled for Supavisor:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// From packages/server/src/database/client.ts
const client = postgres(connectionString, {
  prepare: false,  // Disable prepared statements for Supavisor compatibility
  // ... other options
});
```

**Technical Details:**

* Supavisor pooler operates in transaction mode
* Transaction mode doesn't support prepared statements (PostgreSQL limitation)
* Disabling prepared statements trades minor performance for compatibility
* Only affects Supabase deployments using Supavisor pooler
* Local PostgreSQL and other providers unaffected

**When to Disable:**

* Using Supabase with Supavisor pooler
* Getting XX000 errors on database queries
* Connection string contains `pooler.supabase.com`

**When to Keep Enabled:**

* Local PostgreSQL development
* Direct PostgreSQL connections (no pooler)
* PgBouncer in statement mode
* Neon, Railway, or other providers without transaction pooling

### Streaming WebGPU Backend Issues (RTX 4090)

**Symptom**: WebGPU rendering fails or crashes on RTX 4090 GPUs in streaming mode

**Cause**: ANGLE backend defaulting to GL instead of Vulkan for RTX 4090

**Solution**: Update to latest code (commit 80bb06ed fixed this). ANGLE now uses Vulkan backend for RTX 4090:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// From browser-capture.ts
const gpuBackend = gpuModel?.includes('4090') ? 'vulkan' : 'gl';
const chromeArgs = [
  '--use-angle=vulkan',  // Force Vulkan for RTX 4090
  '--enable-features=Vulkan',
  // ... other flags
];
```

**Technical Details:**

* RTX 4090 requires Vulkan backend for optimal WebGPU performance
* GL backend causes rendering issues and crashes
* Automatically detected based on GPU model string
* Only affects streaming/headless mode with RTX 4090
* Fixed in `packages/server/src/streaming/browser-capture.ts`

**Verification:**
Check Chrome flags in streaming logs for `--use-angle=vulkan` when RTX 4090 is detected.

## Deployment Issues

### Docker Build Failures

**Symptom**: Native module compilation fails in minimal Docker images

**Cause**: Missing build tools in base image

**Solution**: Ensure `build-essential` is installed:

```dockerfile theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# From Dockerfile.server
RUN apt-get update && apt-get install -y \
    build-essential \
    git-lfs \
    python3 \
    python3-pip
```

**Recent Fixes:**

* Added `build-essential` for native module compilation (commit bd2aad9)
* Added `git-lfs` for asset checks (commit 87b3c1a)
* Added `unzip` for bun install (commit 18d6126)

### DNS Resolution Failures

**Symptom**: Cannot resolve external domains in Docker containers

**Cause**: Default DNS servers not working in container environment

**Solution**: Overwrite `resolv.conf` with Google DNS:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# From deploy scripts
echo "nameserver 8.8.8.8" > /etc/resolv.conf
echo "nameserver 8.8.4.4" >> /etc/resolv.conf
```

**Recent Fix:**

* Changed from appending to overwriting `resolv.conf` (commit fd17248)

### Asset Download in Production

**Symptom**: Production builds try to download assets from Git LFS

**Cause**: Asset download script runs in production environment

**Solution**: Set `CI=true` to skip asset download:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# In production environment
CI=true bun run build
```

**Recent Fix:**

* Added `CI=true` check to skip asset download in production (commit 28bf9fb)

### Streaming Dependencies Missing

**Symptom**: Streaming mode fails with missing Playwright or ffmpeg

**Cause**: Playwright dependencies and ffmpeg not installed in deployment image

**Solution**: Install required dependencies:

```dockerfile theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Add to Dockerfile
RUN npx playwright install-deps
RUN apt-get install -y ffmpeg
```

**Recent Fix:**

* Added Playwright deps and ffmpeg for streaming (commit 5920467)

### CORS Issues in Betting Service

**Symptom**: Preflight OPTIONS requests fail from betting frontend

**Cause**: Missing CORS configuration or max-age header

**Solution**: Configure CORS to allow all origins with max-age:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// From keeper CORS config
fastify.register(cors, {
  origin: true,  // Allow all origins
  credentials: true,
  maxAge: 86400,  // 24 hours
});
```

**Recent Fixes:**

* Allow all origins and add CORS max-age (commit 7256b07)
* Allow hyperscape-betting.pages.dev origin (commit fb3b9cd)
* Allow pointer events on stream-bg for Twitch embed interaction (commit 0925171)

## Streaming Issues

### Stream Not Appearing on Twitch/Kick/X

**Symptom**: FFmpeg running but stream not visible on platforms

**Cause**: Incorrect stream keys or RTMP URLs

**Solution**: Verify stream keys are configured correctly:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check environment variables
echo $TWITCH_STREAM_KEY
echo $KICK_STREAM_KEY
echo $X_STREAM_KEY

# Check .env file
cat packages/server/.env | grep STREAM_KEY

# Check PM2 environment
bunx pm2 show hyperscape-duel | grep STREAM_KEY
```

**Common Issues:**

* Stale stream keys in shell environment overriding .env file
* Missing `/app` path in Kick RTMP URL
* Wrong RTMP URL format

**Correct RTMP URLs (as of Feb 26, 2026):**

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Twitch
TWITCH_RTMP_URL=rtmp://live.twitch.tv/app

# Kick (uses RTMPS)
KICK_RTMP_URL=rtmps://fa723fc1b171.global-contribute.live-video.net/app

# X/Twitter
X_RTMP_URL=rtmp://sg.pscp.tv:80/x
```

**Stream Key Management:**

Stream keys must be explicitly unset and re-exported before PM2 start:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# From scripts/deploy-vast.sh
unset TWITCH_STREAM_KEY X_STREAM_KEY X_RTMP_URL KICK_STREAM_KEY KICK_RTMP_URL
source /root/hyperscape/packages/server/.env
bunx pm2 start ecosystem.config.cjs
```

**Debugging:**

Check RTMP status file:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cat packages/server/public/live/rtmp-status.json
```

Check FFmpeg processes:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ps aux | grep ffmpeg
```

Check PM2 logs for streaming keywords:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
bunx pm2 logs hyperscape-duel --lines 200 | grep -iE "rtmp|stream|ffmpeg|destination"
```

**Recent Fixes:**

* Fixed Kick RTMP URL from `ingest.kick.com` to correct IVS endpoint (commit 5dbd239)
* Added all stream keys to CI/CD secrets flow (commit 5dbd239)
* Fixed stream key environment variable handling in deploy scripts (commits 7ee730d, a71d4ba)

### WebGPU Crashes on RTX 5060 Ti

**Symptom**: Chrome crashes or freezes when using WebGPU on RTX 5060 Ti GPUs

**Cause**: Broken Vulkan ICD on RTX 5060 Ti

**Solution**: Use GL ANGLE backend instead of Vulkan:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Set environment variable
CHROME_BACKEND=angle

# Or use Chrome flags
--use-gl=angle --use-angle=gl
```

**Recent Fixes:**

* Removed RTX 5060 Ti from GPU search (commit 30cacb0)
* Switch to GL ANGLE backend for RTX 5060 Ti (commit 0257563)
* WebGPU-safe Chrome flags (removed aggressive GPU opts) (commit f3aa787)

### FFmpeg SIGSEGV in Static Builds

**Symptom**: FFmpeg crashes with segmentation fault when using static builds

**Cause**: Static FFmpeg builds incompatible with some system configurations

**Solution**: Use system FFmpeg instead:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Install system FFmpeg
apt-get install -y ffmpeg

# Verify installation
ffmpeg -version
```

**Recent Fixes:**

* Use system FFmpeg to avoid static build SIGSEGV (commits 55a07bd, 536763d)

### Streaming Black Screen

**Symptom**: Stream shows black screen instead of game

**Causes**:

1. GPU not accessible in headless mode
2. Wrong Chrome backend
3. Missing Xvfb for headful mode

**Solutions**:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Try headful mode with Xvfb
Xvfb :99 -screen 0 1920x1080x24 &
export DISPLAY=:99
bun run dev:streaming

# Or use swiftshader CPU fallback
CHROME_BACKEND=swiftshader bun run dev:streaming
```

**Recent Fixes:**

* Switch back to headful mode for GPU compositing with Xvfb (commit 5e4c6f1)
* Use swiftshader + headless + WebGL for stable streaming (commit ae42beb)

### Chrome Dev Channel Not Found

**Symptom**: Cannot find Chrome Dev channel executable on Vast.ai

**Cause**: Chrome Dev not installed or wrong path

**Solution**: Install Chrome Dev channel:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list
apt-get update
apt-get install -y google-chrome-unstable

# Use in Playwright
executablePath: '/usr/bin/google-chrome-unstable'
```

**Recent Fixes:**

* Use Chrome Dev channel for WebGPU support on Vast.ai (commits ba8bd53, d824163)

### Tauri Build Fails with Empty APPLE\_CERTIFICATE

**Symptom**: Tauri macOS builds fail with `SecKeychainItemImport` error even when not doing a release build

**Cause**: Tauri bundler attempts code signing whenever `APPLE_CERTIFICATE` env var exists, even if empty

**Solution**: Update to latest code (commit 15250d2 fixed this)

**Technical Details:**

* Build workflow now split into Unsigned and Release variants
* Signing env vars only present during actual releases
* Non-release builds no longer set `APPLE_CERTIFICATE=''`
* macOS unsigned builds use `--no-bundle` instead of `--bundles app` (app bundle type is macOS-only)
* iOS builds are release-only (unsigned iOS always fails with "Signing requires a development team")
* Windows builds include retry logic for transient NPM registry errors
* File: `.github/workflows/build-app.yml`
* Commit: 15250d2 (Feb 25, 2026)

### Linux/Windows Desktop Builds Fail in CI

**Symptom**: Tauri desktop builds fail on Linux/Windows with "app bundle type is macOS-only" error

**Cause**: `--bundles app` flag is macOS-specific, causes Linux/Windows builds to fail

**Solution**: Update to latest code (commit f19a704 fixed this)

**Technical Details:**

* Replaced `--bundles app` with `--no-bundle` for unsigned builds
* `app` bundle type is macOS-only, causing Linux/Windows to fail
* Made `beforeBuildCommand` cross-platform using Node.js instead of Unix shell `test` command
* Split artifact upload: release builds upload bundles, unsigned builds upload raw binaries
* File: `.github/workflows/build-app.yml`
* Commit: f19a704 (Feb 26, 2026)

### Cloudflare Origin Lock Blocking API Access

**Symptom**: Frontend cannot access API directly, getting origin lock errors

**Cause**: Cloudflare origin lock preventing direct frontend API access

**Solution**: Update to latest code (commit 3ec9826 fixed this)

**Technical Details:**

* Disabled cloudflare origin lock in server configuration
* Allows direct API access from frontend
* File: `packages/server/` (server configuration)
* Commit: 3ec9826 (Feb 25, 2026)

### Cloudflare Pages Worker Deployment Error

**Symptom**: Cloudflare Pages deployment fails with worker deployment error

**Cause**: Missing build output directory specification in Cloudflare configuration

**Solution**: Update to latest code (commit 1af02ce fixed this)

**Technical Details:**

* Specified pages build output dir in Cloudflare configuration
* Cloudflare Pages deployments now succeed
* Commit: 1af02ce (Feb 25, 2026)

### Cloudflare Wrangler Configuration Issues

**Symptom**: Cloudflare Pages deployment fails with configuration errors or uses wrong directive

**Cause**: Incorrect wrangler.toml configuration for Pages deployment

**Solution**: Update to latest code (commit 42a1a0e fixed this)

**Technical Details:**

* Switched from `pages_build_output_dir` to `[assets]` directive
* Works better with `wrangler deploy` for static asset hosting
* Deploy command should be empty in Cloudflare dashboard
* File: `packages/client/wrangler.toml`
* Commit: 42a1a0e (Feb 26, 2026)

### Root Wrangler Config Conflicts with Pages Project

**Symptom**: Cloudflare deployment confusion between Worker and Pages projects

**Cause**: Root `wrangler.toml` named "hyperscape-betting" conflicts with "hyperscape" Pages project

**Solution**: Update to latest code (commit f19a704 fixed this)

**Technical Details:**

* Removed root `wrangler.toml` to avoid deployment confusion
* Correct Pages configuration is in `packages/client/wrangler.toml`
* To complete fix, update Cloudflare Dashboard:
  1. Workers & Pages → hyperscape (Worker) → Settings → disconnect GitHub
  2. Workers & Pages → hyperscape (Pages) → Settings → connect GitHub
* Commit: f19a704 (Feb 26, 2026)

### Assets Not Found After Fresh Clone

**Symptom**: Server fails to start with "No world areas defined" or manifest validation errors

**Cause**: Assets directory not cloned from HyperscapeAI/assets repository

**Solution**: Assets are automatically cloned during `bun install`. If missing:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Manual asset sync
bun run assets:sync

# Or re-run install
bun install
```

**Technical Details:**

* Assets are sourced from [HyperscapeAI/assets](https://github.com/HyperscapeAI/assets) repository
* `scripts/ensure-assets.mjs` runs during `bun install` postinstall hook
* Local dev: Full clone with LFS (\~200MB)
* CI: Shallow clone without LFS (manifests only, \~1MB)
* Entire `packages/server/world/assets/` directory is gitignored

**Verification:**

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check if assets exist
ls packages/server/world/assets/manifests/

# Should see:
# items/, gathering/, recipes/, npcs.json, stores.json, etc.
```

### Model Cache Issues

**Symptom**: Missing objects in models (e.g., altars), white/wrong colored textures after restart, or grey trees in WebGPU builds

**Cause**: Bugs in IndexedDB processed model cache (fixed in PR #935, commit c98f1cc, Feb 25, 2026)

**Solution**: Update to latest code. The cache now uses:

* Object-identity mapping instead of name-based lookup (fixes missing objects)
* Raw RGBA pixel extraction for textures (fixes lost textures)
* Duck-type property checks for materials (fixes grey trees in WebGPU)

**Temporary Workaround**: Disable the cache:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// In browser console
localStorage.setItem('disable-model-cache', 'true');
```

**Technical Details:**

**Bug #1 - Missing Objects (e.g., altars):**

* **Problem**: `serializeNode` used `findIndex`-by-name to map hierarchy nodes to mesh data. Models with duplicate mesh names (common: "", "Cube", "Cube") all resolved to the same index. During deserialization, Three.js `add()` auto-removes from previous parent, so only the last reference survived.
* **Fix**: Use `Map<Object3D, number>` identity map built during traversal instead of name-based lookup
* **Impact**: All objects in models now render correctly, including complex multi-mesh models like altars

**Bug #2 - Lost Textures (white/wrong colors after restart):**

* **Problem**: Textures were serialized as ephemeral `blob:` URLs but never reloaded during deserialization. After page refresh, blob URLs became invalid, leaving models white or incorrectly colored.
* **Fix**: Extract raw RGBA pixels via canvas `getImageData` (synchronous) and restore as `THREE.DataTexture` — no async loading race conditions
* **Impact**: Textures persist correctly across page refreshes, models maintain correct colors

**Bug #3 - Grey Trees in WebGPU Builds:**

* **Problem**: `createDissolveMaterial` used `instanceof MeshStandardMaterial` which fails for `MeshStandardNodeMaterial` in the WebGPU build where they are separate classes
* **Fix**: Replaced with duck-type property check (`src.color && src.roughness !== undefined`)
* **Impact**: Tree materials render correctly in WebGPU builds instead of appearing grey

**Additional Improvements:**

* Added `localStorage.setItem('disable-model-cache', 'true')` to bypass cache for debugging
* Error logging on IndexedDB `put`/transaction failures
* Cache version bumped to 3 to invalidate broken entries
* File: `packages/shared/src/utils/rendering/ModelCache.ts`
* PR: #935 | Commit: c98f1cc (Feb 25, 2026)

See [Model Cache System](/wiki/engine/model-cache) for complete details.

### Terrain Height Offset Issues

**Symptom**: Players/entities positioned 50m above or below expected terrain height

**Cause**: Tile index and grid index calculations didn't account for PlaneGeometry's centered coordinate system

**Solution**: Update to latest code (commit 21e0860 fixed this)

**Technical Details:**

* **Tile Index Bug**: `Math.floor(worldX/TILE_SIZE)` doesn't account for centered geometry
  * PlaneGeometry uses \[-50, +50] range, not \[0, 100]
  * Fixed with `worldToTerrainTileIndex()` canonical helper
* **Grid Index Bug**: Formula omitted `halfSize` offset from PlaneGeometry's centered range
  * Fixed with `localToGridIndex()` canonical helper
  * Applied to both `getHeightAtCached()` and `getTerrainColorAt()`
* **Key Format Bug**: `getTerrainColorAt()` had comma-vs-underscore typo preventing tile lookups
  * Changed from `${tileX},${tileZ}` to `${tileX}_${tileZ}`

**Impact**: Terrain height queries now return correct values, fixing player/entity positioning

**File**: `packages/shared/src/systems/shared/world/TerrainSystem.ts`

### Cyclic Dependency Build Errors

**Symptom**: TypeScript errors about missing types from `@hyperscape/shared` or `@hyperscape/procgen`, or Turbo detecting circular dependencies

**Cause**: Circular dependencies between packages during clean builds

**Solution**: Update to latest code (commits f355276, 3b9c0f2, 05c2892 fixed shared/procgen cycle). The build system handles this automatically with resilient patterns:

* **procgen ↔ shared**: Fully broken cycle via peerDependencies + devDependencies
  * Commit f355276: Moved procgen from dependencies to optional peerDependencies in shared
  * Commit 3b9c0f2: Removed cross-references entirely from both package.json files
  * Commit 05c2892: Added procgen as devDependency in shared for TypeScript type resolution
  * Turbo treats peerDependencies as graph edges, so peerDependencies alone was insufficient
  * devDependencies are not followed by turbo's `^build` topological ordering (no cycle)
  * Imports still resolve at runtime via bun workspace resolution (both packages always installed together)
* **plugin-hyperscape ↔ shared**: Uses `--skipLibCheck` in declaration generation
* Packages produce partial output sufficient for downstream consumers

If you still see errors:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Build shared first to create dist/ directory
cd packages/shared
bun run build

# Then build dependent packages
cd ../procgen
bun run build

cd ../plugin-hyperscape
bun run build
```

**Recent Fixes:**

* Commit 05c2892 (Feb 26, 2026): Added procgen as devDependency for TypeScript type resolution
* Commit 3b9c0f2 (Feb 26, 2026): Fully broke shared↔procgen cycle by removing cross-references
* Commit f355276 (Feb 25, 2026): Initial fix moving procgen to peerDependencies
* Files: `packages/shared/package.json`, `packages/procgen/package.json`

### Shared/Procgen Type Resolution Issues

**Symptom**: TypeScript can't find `@hyperscape/procgen` module declarations during type checking in shared package

**Cause**: procgen removed from dependencies to break circular dependency, but TypeScript still needs it for type resolution

**Solution**: Update to latest code (commit 05c2892 fixed this)

**Technical Details:**

* Added procgen as `devDependency` in shared's package.json
* devDependencies are not followed by turbo's `^build` topological ordering (doesn't create cycle)
* Ensures bun links the package so TypeScript can find module declarations during type checking
* Imports still resolve at runtime via bun workspace resolution
* File: `packages/shared/package.json`
* Commit: 05c2892 (Feb 26, 2026)

### ESLint Crashes in Asset Forge

**Symptom**: ESLint crashes with errors about `import/order` rule using removed `sourceCode.getTokenOrCommentBefore` API

**Cause**: `eslint-plugin-import@2.32.0` is incompatible with ESLint 10 - uses removed API

**Solution**: Update to latest code (commits cadd3d5, b5c762c fixed this)

**Technical Details:**

* Disabled cascaded `import/order` rule in asset-forge's flat config
* Used `eslint src` instead of `eslint . --ext .ts,.tsx` to avoid linting entire directory
* Prevents eslint-plugin-import's import/order rule from crashing
* Files: `packages/asset-forge/eslint.config.mjs`, `packages/asset-forge/package.json`
* Commits: cadd3d5, b5c762c (Feb 26, 2026)

### Cloudflare Pages Deploy Fails with Wrangler Config Error

**Symptom**: Cloudflare Pages deployment fails with configuration errors

**Cause**: Incorrect wrangler.toml configuration for Pages deployment

**Solution**: Update to latest code (commit 42a1a0e fixed this)

**Technical Details:**

* Switched from `pages_build_output_dir` to `[assets]` directive
* Works better with `wrangler deploy` for static asset hosting
* Deploy command should be empty in Cloudflare dashboard
* File: `packages/client/wrangler.toml`
* Commit: 42a1a0e (Feb 26, 2026)

### Root Wrangler Config Conflicts with Pages Project

**Symptom**: Cloudflare deployment confusion between Worker and Pages projects

**Cause**: Root `wrangler.toml` named "hyperscape-betting" conflicts with "hyperscape" Pages project

**Solution**: Update to latest code (commit f19a704 fixed this)

**Technical Details:**

* Removed root `wrangler.toml` to avoid deployment confusion
* Correct Pages configuration is in `packages/client/wrangler.toml`
* To complete fix, update Cloudflare Dashboard:
  1. Workers & Pages → hyperscape (Worker) → Settings → disconnect GitHub
  2. Workers & Pages → hyperscape (Pages) → Settings → connect GitHub
* Commit: f19a704 (Feb 26, 2026)

### Linux/Windows Desktop Builds Fail in CI

**Symptom**: Tauri desktop builds fail on Linux/Windows with "app bundle type is macOS-only" error

**Cause**: `--bundles app` flag is macOS-specific, causes Linux/Windows builds to fail

**Solution**: Update to latest code (commit f19a704 fixed this)

**Technical Details:**

* Replaced `--bundles app` with `--no-bundle` for unsigned builds
* `app` bundle type is macOS-only, causing Linux/Windows to fail
* Made `beforeBuildCommand` cross-platform using Node.js instead of Unix shell `test` command
* Split artifact upload: release builds upload bundles, unsigned builds upload raw binaries
* File: `.github/workflows/build-app.yml`
* Commit: f19a704 (Feb 26, 2026)

### Mage Agents Not Attacking in Duels

**Symptom**: Mage agents stand idle during duels, no spell projectiles visible

**Cause**: Combat state key mismatch between `CombatStateService` and `EmbeddedHyperscapeService`

**Solution**: Update to latest code (PR #933 fixed this)

**Technical Details:**

* `CombatStateService` syncs abbreviated keys (`data.c`/`data.ct`) for network efficiency
* `EmbeddedHyperscapeService.getGameState()` only read full keys (`data.inCombat`/`data.combatTarget`)
* DuelCombatAI always saw `inCombat=false` and flooded `executeAttack` every tick
* Fixed by reading both abbreviated and full keys:
  ```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
  inCombat: !!(data.inCombat || data.combatTarget || data.c || data.ct),
  currentTarget: (data.combatTarget as string) || (data.ct as string) || null,
  ```
* File: `packages/server/src/eliza/EmbeddedHyperscapeService.ts`
* Commit: 82ff784 (Feb 25, 2026)

### Duplicate Magic Projectiles

**Symptom**: Magic attacks spawn two projectiles, runes consumed twice

**Cause**: TOCTOU race condition in `handleMagicAttack` cooldown claim

**Solution**: Update to latest code (PR #933 fixed this)

**Technical Details:**

* Cooldown was checked early but claimed after async `consumeRunesForSpell` call
* With combat state bug flooding attacks, two concurrent invocations could both pass cooldown check
* Fixed by moving cooldown claim before async rune consumption:
  ```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
  // Claim cooldown BEFORE async rune consumption
  this.ctx.combatSystem.setNextAttackTick(attackerId, nextAttackTick);
  this.ctx.combatSystem.enterCombat(attackerId, targetId, weaponType);

  // THEN consume runes (async operation)
  await this.ctx.runeService.consumeRunesForSpell(attackerId, spell);
  ```
* File: `packages/shared/src/systems/shared/combat/handlers/MagicAttackHandler.ts`
* Commit: 82ff784 (Feb 25, 2026)

### Players Sinking Into Duel Arena Floors

**Symptom**: Players/agents appear to sink \~0.4m into duel arena floors, grass grows through floor surfaces

**Cause**: Flat zones were removed from terrain system, causing `getHeightAt()` to return raw procedural terrain height instead of floor-level height

**Solution**: Update to latest code (PR #911 fixed this)

**Technical Details:**

* `DuelArenaVisualsSystem` now registers flat zones programmatically for all 8 floor areas
* Affected areas: 6 arenas (2×3 grid), lobby (60×30), hospital (20×15)
* Terrain height queries now return correct floor-level values
* Terrain mesh is carved under floors via flat zone registration
* File: `packages/shared/src/systems/client/DuelArenaVisualsSystem.ts`
* Commit: 7a60135 (Feb 25, 2026)

### Cannot Click to Move in Duel Arenas

**Symptom**: Click targets go underground in duel arenas, players cannot move

**Cause**: Building footprint validation rejecting arena-floor raycast hits

**Solution**: Update to latest code (commit 2435423 fixed this)

**Technical Details:**

* `RaycastService` now skips building footprint validation for arena-floor raycast hits
* File: `packages/shared/src/systems/client/interaction/services/RaycastService.ts`
* Commit: 2435423 (Feb 24, 2026)

### Duel Arena Minimap Shows Black Holes

**Symptom**: Minimap shows duel arenas as black holes, unusable in arena area

**Cause**: Arena/lobby/hospital floor meshes had layer 0 disabled

**Solution**: Update to latest code (commit 2435423 fixed this)

**Technical Details:**

* Enabled layer 0 on arena/lobby/hospital floor meshes so minimap camera can render them
* File: `packages/shared/src/systems/client/DuelArenaVisualsSystem.ts`
* Commit: 2435423 (Feb 24, 2026)

### Victory Emote Overridden by Combat Cleanup

**Symptom**: Winning agent's wave emote is immediately overwritten by idle animation after duel victory

**Cause**: Combat animation system's stale idle resets override victory emote

**Solution**: Update to latest code (commit 645137386 fixed this)

**Technical Details:**

* **Delayed Victory Emote**: Victory emote now delayed by 600ms after combat resolution
  * Allows all death/combat cleanup (emote resets, combat state teardown, scheduled animation resets) to finish first
  * Without delay, victory emote was immediately overwritten by stale idle resets from combat animation system
* **Emote Reset in stopCombat**: Reset emote to idle when agents teleport out
  * Ensures wave animation stops when agents leave arena
  * Prevents emote from persisting after duel ends
* **Entity Data Sync**: Set emote on server entity so future entity sync includes it
  * Ensures victory emote is part of entity state
  * Broadcasts to all clients via `entityModified` packet
* File: `packages/server/src/systems/StreamingDuelScheduler/managers/DuelOrchestrator.ts`
* Commit: 645137386 (Feb 25, 2026)

### WebSocket Type Errors

**Symptom**: TypeScript errors about missing `removeAllListeners` and `on` methods on WebSocket connections

**Cause**: Fastify websocket connections use `ws` library type, not browser WebSocket

**Solution**: Update to latest code (commit efba5a0 fixed this)

**Technical Details:**

* Import proper WebSocket type from `ws` library: `import type { WebSocket } from 'ws'`
* Fastify's `@fastify/websocket` uses the `ws` library under the hood
* Browser WebSocket type doesn't have Node.js EventEmitter methods
* File: `packages/server/src/systems/ServerNetwork/index.ts`
* Commit: efba5a0 (Feb 26, 2026)

### Asset-Forge TypeScript Strict Mode Errors

**Symptom**: TypeScript errors in asset-forge about implicit `any` types in traverse callbacks

**Cause**: TypeScript strict mode requires explicit types for callback parameters

**Solution**: Update to latest code (commits 82f97da, 42e52af fixed this)

**Technical Details:**

* **Traverse Callbacks** (commit 82f97da): Added type annotations for traverse callbacks
  * Example: `(child: THREE.Object3D) => { ... }` instead of `(child) => { ... }`
  * Required by TypeScript strict mode
* **Module Resolution** (commit 42e52af): Updated to `moduleResolution: "bundler"`
  * Three.js WebGPU subpath requires `bundler` or `node16` module resolution
  * Previous `'node'` setting couldn't resolve the exports map correctly
  * File: `packages/asset-forge/tsconfig.json`

### Type Safety in Core Game Logic

**Symptom**: Explicit `any` types in core game logic causing type safety issues

**Cause**: Legacy code with untyped variables

**Solution**: Update to latest code (commit d911359 fixed this)

**Technical Details:**

* **tile-movement.ts**: Removed 13 `any` casts by properly typing BuildingCollisionService and ICollisionMatrix method calls
* **proxy-routes.ts**: Replaced `any` with proper types:
  * Error handlers: `unknown` for caught errors
  * WebSocket message handlers: `Buffer | string` for message data
  * Error events: `Error` type
* **ClientGraphics.ts**: Added cast for `setupGPUCompute` after WebGPU verification
  * WebGPU is now required, so cast is safe after verification check
* **Remaining `any` types** (acceptable):
  * TSL shader code (ProceduralGrass.ts) - @types/three limitation
  * Browser polyfills (polyfills.ts) - intentional mock implementations
  * Test files - acceptable for test fixtures
* Commit: d911359 (Feb 26, 2026)

### Duplicate Teleport VFX

**Symptom**: Three teleport effects play when agents exit duel arena (should only be one)

**Cause**: Race condition between `clearDuelFlagsForCycle()` and `ejectNonDuelingPlayersFromCombatArenas()`

**Solution**: Update to latest code (commit 7bf0e14 fixed this)

**Technical Details:**

* Removed premature `clearDuelFlagsForCycle()` in `endCycle()`
* Flags now stay true until `cleanupAfterDuel()` completes teleports via microtask
* Prevents `DuelSystem` from seeing agents with `inStreamingDuel=false` still in arena
* `suppressEffect` flag now forwarded through ServerNetwork → ClientNetwork → VFX system
* Mid-fight proximity corrections suppressed, arena exit effects visible
* Removed duplicate `PLAYER_TELEPORTED` emit from `PlayerRemote.modify()` and local player path
* File: `packages/server/src/systems/StreamingDuelScheduler/index.ts`
* Commit: 7bf0e14 (Feb 25, 2026)

### Teleport Beam Clipping Through Floor

**Symptom**: Teleport beam base penetrates through floor instead of emerging from rune circle

**Cause**: No fade at beam base, full opacity from bottom to top

**Solution**: Update to latest code (commit ceb8909 fixed this)

**Technical Details:**

* Added `bottomFade` in TSL beam shader
* Soft fade at beam base: `sub(1.0, max(sub(1.0, mul(yNorm, 2.0)), 0.0))`
* Beam now emerges from rune circle smoothly
* File: `packages/shared/src/systems/client/ClientTeleportEffectsSystem.ts`
* Commit: ceb8909 (Feb 25, 2026)

### Streaming Disconnects After 2 Minutes

**Symptom**: RTMP stream disconnects or restarts every \~2 minutes

**Cause**: CDP stall threshold too aggressive (2 intervals = 60s)

**Solution**: Update to latest code (commit 14a1e1b fixed this)

**Technical Details:**

* **CDP Stall Threshold**: Increased from 2 → 4 intervals (120s) to reduce false restarts
* **Soft CDP Recovery**: Restart screencast without browser/FFmpeg teardown
  * No stream gap during recovery
  * Preserves browser state
* **FFmpeg Restart Attempts**: Increased from 5 → 8 max attempts
* **Recovery Counter Reset**: `resetRestartAttempts()` for successful recovery
* **Capture Recovery Failures**: Increased default from 2 → 4 max failures
* File: `packages/server/src/streaming/`
* Commit: 14a1e1b (Feb 25, 2026)

### WebGPU Renderer Fails to Initialize

**Symptom**: WebGPU renderer initialization fails with "Required limits not supported" error

**Cause**: GPU doesn't support `maxTextureArrayLayers: 2048`

**Solution**: Update to latest code (commit 14a1e1b fixed this)

**Technical Details:**

* **Best-Effort Limits**: Renderer now tries `maxTextureArrayLayers: 2048` first
  * Retries with default limits if GPU rejects
  * Always WebGPU, never WebGL fallback
  * Prevents renderer init failures on GPUs with lower limits
* **Graceful Degradation**: Falls back to GPU's supported limits automatically
* **No WebGL Fallback**: Ensures consistent rendering pipeline (WebGPU only)
* File: `packages/shared/src/utils/rendering/RendererFactory.ts`
* Commit: 14a1e1b (Feb 25, 2026)

### NPM 403 Forbidden Errors in CI

**Symptom**: `bun install` fails in GitHub Actions with npm 403 Forbidden errors

**Cause**: npm rate-limits GitHub Actions IP ranges

**Solution**: Update to latest code (commits 7c9ff6c, 08aa151 fixed this)

**Technical Details:**

* Retry with backoff: up to 5 attempts with increasing delays (15s, 30s, 45s, 60s, 75s)
* Use `--frozen-lockfile` in all workflows to prevent fresh package resolution
* Ensures bun uses only committed lockfile
* Files: `.github/workflows/*.yml`
* Commits: 7c9ff6c, 08aa151 (Feb 25, 2026)

### Tauri macOS Build Fails with Empty Certificate

**Symptom**: Tauri macOS builds fail with `SecKeychainItemImport` error even for non-release builds

**Cause**: Tauri bundler attempts code signing whenever `APPLE_CERTIFICATE` env var exists, even if empty

**Solution**: Update to latest code (commit 15250d2 fixed this)

**Technical Details:**

* Build workflow split into Unsigned and Release variants
* Signing env vars only present during actual releases
* Non-release builds no longer set `APPLE_CERTIFICATE=''`
* macOS unsigned builds use `--bundles app` to skip DMG creation
* iOS builds are release-only (unsigned iOS always fails)
* Windows builds have retry logic for transient NPM registry errors
* File: `.github/workflows/build-app.yml`
* Commit: 15250d2 (Feb 25, 2026)

## Getting Help

1. Check [GitHub Issues](https://github.com/HyperscapeAI/hyperscape/issues)
2. Search existing discussions
3. Open a new issue with:
   * Error message
   * Steps to reproduce
   * Environment details (OS, Node version, Bun version)
