Skip to content

Troubleshooting

Common errors encountered when developing HOS drivers, with causes and solutions.

All error responses from the server follow this envelope:

json
{
    "ok": false,
    "code": "DRIVER_WS_VALIDATION_ERROR",
    "error": "Human-readable error message",
    "details": ["Specific detail 1", "Specific detail 2"],
    "protocolVersion": 1
}

Registration Errors

Registration errors occur during the driver.register handshake. See Driver Lifecycle for the full registration flow.

Missing protocolVersion

Error: "protocolVersion is required for driver.register."

Detail: "Set protocolVersion: 1."

Cause: The driver.register message did not include protocolVersion.

Solution: The HOS SDK sets this automatically. If calling register manually, include protocolVersion: 1 in the params:

typescript
// Manual register message (not needed when using HosDriver)
{
    method: 'driver.register',
    params: {
        driverKey: 'MY_DRIVER',
        instanceId: 'my-driver:001',
        protocolVersion: 1,  // required
    }
}

Invalid driverKey

Error: "Invalid driver.register payload."

Detail: "driverKey must match [A-Z0-9_] and be 2-64 chars."

Cause: The driverKey contains invalid characters or is too short/long.

Solution: Use only uppercase letters, digits, and underscores. The SDK auto-uppercases the key, but it must still be 2-64 characters.

typescript
// Correct
new HosDriver({ driverKey: 'MY_DRIVER', ... })
new HosDriver({ driverKey: 'HUE_BRIDGE', ... })

// Invalid — contains hyphen and lowercase
new HosDriver({ driverKey: 'my-driver!', ... })

Invalid instanceId

Error: "Invalid driver.register payload."

Detail: "instanceId must match [a-zA-Z0-9:_-] and be 1-128 chars."

Cause: The instanceId contains invalid characters or is empty/too long.

Solution: Use letters, digits, colons, underscores, or hyphens:

typescript
// Correct
new HosDriver({ instanceId: 'my-driver:001', ... })
new HosDriver({ instanceId: 'hue-bridge_01', ... })

// Invalid — contains spaces and special chars
new HosDriver({ instanceId: 'my driver @home', ... })

Unregistered Session

Error: "Unregistered websocket session."

Detail: "driver.register is required before sending events."

Cause: Tried to send events (e.g. DEVICE_DISCOVERED) before completing registration.

Solution: Wait for the 'registered' event before calling sendEvent(). With autoRegister: true (default), the SDK handles registration automatically — listen for the registered event:

typescript
const driver = new HosDriver({ driverKey: 'MY_DRIVER', instanceId: 'my-driver:001' });

// Wait for registered before sending events
driver.on('registered', () => {
    driver.sendEvent('DEVICE_DISCOVERED', {
        device_id: 'my-device-001',
        data: { name: 'My Device', deviceType: 'light' },
    });
});

driver.connect();

Message Errors

Invalid JSON

Error: "Invalid JSON"

Cause: The WebSocket message was not valid JSON.

Solution: This should not happen when using the SDK — sendEvent() serializes messages automatically with JSON.stringify(). If sending raw WebSocket messages, ensure the payload is valid JSON:

typescript
// Correct — SDK handles serialization
driver.sendEvent('STATE_UPDATE', { device_id: 'my-device', data: { power: true } });

// If sending raw — must be valid JSON
ws.send(JSON.stringify({ event: 'STATE_UPDATE', device_id: 'my-device', data: { power: true } }));

Invalid Event Payload

Error: "Invalid {EVENT_NAME} payload."

Common details:

  • "device_id is required." — missing device_id field
  • "data must be a non-null object." — missing or invalid data field

Cause: The event message is missing required fields.

Solution: Every event except DEVICE_REMOVED requires both device_id (string) and data (object). Check that you pass both to sendEvent():

typescript
driver.sendEvent('STATE_UPDATE', {
    device_id: 'my-device-001',  // required
    data: { power: true },       // required
});

DEVICE_REMOVED only requires device_id:

typescript
driver.sendEvent('DEVICE_REMOVED', {
    device_id: 'my-device-001',
});

Connection Errors

See Getting Started to verify your environment setup before debugging connection issues.

Connection Refused

Symptom: 'error' event fires with ECONNREFUSED

Cause: HOS server is not running, or the WebSocket URL/port is wrong.

Solution:

  1. Verify the HOS server is running (npm run server from the repo root)
  2. Check HOS_WS_URL — default is ws://localhost:8080/driver
  3. Verify the port matches the server's DRIVER_WS_PORT configuration
typescript
// Check your wsUrl
const driver = new HosDriver({
    driverKey: 'MY_DRIVER',
    instanceId: 'my-driver:001',
    wsUrl: process.env.HOS_WS_URL ?? 'ws://localhost:8080/driver',
});

driver.on('error', (err) => {
    console.error('Connection error:', err.message);
    // ECONNREFUSED means server is not reachable
});

Session Replaced (Close Code 4001)

Symptom: 'disconnected' event with code: 4001, followed by 'error' event with "Session replaced by new connection"

Cause: Another driver instance connected with the same driverKey + instanceId combination. The server closed the older session.

Solution: Each running instance MUST have a unique instanceId. If running multiple instances of the same driver type, use distinct instanceIds:

typescript
// Instance 1
new HosDriver({ driverKey: 'HUE_BRIDGE', instanceId: 'hue-bridge:001' })

// Instance 2
new HosDriver({ driverKey: 'HUE_BRIDGE', instanceId: 'hue-bridge:002' })

Note: The SDK does NOT reconnect after a 4001 close — this is intentional. A 4001 indicates a configuration conflict, not a transient network error.


Reconnect Behavior

Symptom: Driver keeps disconnecting and reconnecting

The SDK reconnects automatically with exponential backoff:

AttemptDelay
11s (+/-10% jitter)
22s (+/-10% jitter)
34s (+/-10% jitter)
48s (+/-10% jitter)
5+30s (maximum, +/-10% jitter)

The delay resets to 1s after a successful connection is established.

To disable reconnect:

typescript
const driver = new HosDriver({
    driverKey: 'MY_DRIVER',
    instanceId: 'my-driver:001',
    reconnect: false,
});

To adjust timing:

typescript
const driver = new HosDriver({
    driverKey: 'MY_DRIVER',
    instanceId: 'my-driver:001',
    reconnectBaseMs: 2000,   // Start at 2s instead of 1s
    reconnectMaxMs: 60000,   // Cap at 60s instead of 30s
});

If the driver cannot reconnect, verify the server is still running and that no authentication or session conflict (4001) has occurred.


What's Next