homebridge/homebridge · error · StatusResponseError

Status.Failure

Status.Failure

Error message

Failed to change system mode: ${message}

What it means

When the thermostat's systemMode attribute changes (off/heat/cool/auto), the behavior emits a `systemModeChange` event to the plugin-registered handler. If that handler throws any non-Matter error, it is wrapped as `StatusResponseError('Failed to change system mode: <message>', Status.Failure)` via the reactTo event path. The Matter attribute transaction aborts with Failure instead of crashing the endpoint.

Source

Thrown at src/matter/behaviors/ThermostatBehavior.ts:71

        endpointId,
        'thermostat',
        'systemModeChange',
        { systemMode: value, oldSystemMode: oldValue },
      )

      // Sync state to cache
      registry.syncStateToCache(endpointId, 'thermostat', { systemMode: value })
    } catch (error) {
      // If user handler already threw a StatusResponseError, propagate it as-is
      // This sends a proper Matter protocol error response to the controller
      if (MatterStatus.isMatterProtocolError(error)) {
        throw error
      }

      // For other errors, wrap in appropriate StatusResponseError
      // This prevents the endpoint from crashing and keeps the device online
      const message = error instanceof Error ? error.message : String(error)
      throw new StatusResponseError(`Failed to change system mode: ${message}`, Status.Failure)
    }
  }

  async #handleOccupiedHeatingSetpointChanging(value: unknown): Promise<void> {
    const endpointId = this.endpoint.id
    const registry = this.getRegistry()
    // Using 'as any' because occupiedHeatingSetpoint is feature-dependent (Heating feature)
    const oldValue = (this.state as any).occupiedHeatingSetpoint

    try {
      // Execute user handler
      await registry.executeHandler(
        endpointId,
        'thermostat',
        'occupiedHeatingSetpointChange',
        { occupiedHeatingSetpoint: value as number, oldOccupiedHeatingSetpoint: oldValue },
      )

View on GitHub (pinned to edf5493034)

Solutions

  1. Fix the plugin error reported after 'Failed to change system mode: '.
  2. Advertise only the modes the hardware supports (Thermostat cluster mode feature flags) so controllers never request unsupported modes.
  3. Throw StatusResponseError/InvalidCommand from the plugin for unsupported mode transitions.
  4. Check HVAC cloud credentials/connectivity in the plugin when the failure is intermittent.
  5. Serialize mode-change handling in the plugin to avoid races between controllers.

Example fix

// before
systemModeChange({ systemMode }) { return this.hvac.setMode(systemMode) } // throws on 'cool'
// after
systemModeChange({ systemMode }) {
  if (!this.supportedModes.has(systemMode)) {
    throw new StatusResponseError('mode not supported by hvac', Status.InvalidCommand)
  }
  return this.hvac.setMode(systemMode)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// gate mode changes on actual hardware support
const SUPPORTED_SYSTEM_MODES = new Set([0, 4, 3, 1]) // off, cool, heat, auto per device
if (!SUPPORTED_SYSTEM_MODES.has(requestedSystemMode)) {
  throw new StatusResponseError('system mode not supported by hvac', Status.InvalidCommand)
}
if (!this.hvac.online) throw new StatusResponseError('hvac offline', Status.Failure)

Type guard

function isMatterProtocolError(e: unknown): e is StatusResponseError {
  return e instanceof StatusResponseError
    || (typeof e === 'object' && e !== null && 'status' in e && typeof (e as any).code === 'number')
}

Try / catch

try {
  await registry.executeHandler(endpointId, 'thermostat', 'systemModeChange', payload)
} catch (e) {
  logger.warn('system mode change failed:', e instanceof Error ? e.message : e)
  if (isMatterProtocolError(e)) throw e
  throw new StatusResponseError(`Failed to change system mode: ${String(e)}`, Status.Failure)
}

Prevention

When it happens

Trigger: A controller sets the thermostat mode in Home and the plugin's `thermostat.systemModeChange` handler rejects — e.g. the HVAC API refuses 'cool' when only heating is wired, an auth token expired, or the handler throws during a write to the physical thermostat.

Common situations: Plugins backing thermostats with partial mode support receiving a mode the hardware cannot do; cloud HVAC API downtime; mode changes made simultaneously from two controllers racing in the plugin's state.

Related errors


AI-assisted analysis of homebridge/homebridge@edf5493034 (2026-08-30). Data as JSON: /api/errors/67ee7108be9d85b4. Report an issue: GitHub.