Skip to content
These pages describe Stewart 1.x. Read the docs for the latest release, and how to upgrade.

Error reference

Every failure Stewart reports has a reason. The exception class names the part of Stewart that failed, and $e->reason names the failure: StoreError::TypeMismatch belongs to a StoreException. Words in braces are filled in from $e->context. Handling errors explains how to catch them.

Thrown as HaClientException.

Reason Message What to do
ClosedLocally The connection was closed locally while {phase}.
ConnectTimedOut Connecting to Home Assistant at {url} timed out after {timeout}. Check home_assistant.url. Test the URL from the container: make sh, then wget -qO- http://your-host:8123.
ConnectFailed Could not connect to Home Assistant at {url}: {cause} Check STEWART_HOME_ASSISTANT__URL; inside the container localhost is the container, so use the host name, IP or host.docker.internal.
NotConnected Not connected to Home Assistant.
SendFailed Failed to send {command}: {cause}
ClosedDuringAuthentication Home Assistant closed the connection during authentication. Check STEWART_HOME_ASSISTANT__TOKEN, then the Home Assistant log from the same moment.
ConnectionDropped The connection to Home Assistant was lost ({detail}).
MessageTooLarge Home Assistant sent a message over the size limit, so the connection was dropped. Raise home_assistant.message_size_limit or home_assistant.frame_size_limit.
ProtocolViolation Home Assistant sent {detail}.
CommandTimedOut Home Assistant did not answer {command} within {timeout}.
CommandRejected {command} failed: {detail}
CommandUnauthorized {command} failed: {detail}
CommandUnencodable {command} could not be encoded: {cause}
UnexpectedGreeting Expected auth_required from Home Assistant, got “{receivedType}”.
TokenRejected Home Assistant rejected the access token: {reason}. Check STEWART_HOME_ASSISTANT__TOKEN. Create a long-lived access token on your Home Assistant profile, under Security, as an administrator.
AdministratorRequired Home Assistant refused the event subscription; the access token must belong to an administrator. Update STEWART_HOME_ASSISTANT__TOKEN. Create the token from a Home Assistant administrator.
EventBacklogExceeded Event handlers are more than {limit} events behind; dropping the connection.
EventQueueReentered An event callback cannot wait for the event queue it is being delivered by.
UrlInvalid “{url}” is not a Home Assistant URL; use http://, https://, ws:// or wss:// and a host. For example ws://homeassistant.local:8123/api/websocket in STEWART_HOME_ASSISTANT__URL.
ComponentInstanceInvalid “{instance}” is not an instance name; use lowercase letters, digits and underscores, starting with a letter, up to 64 characters.

Thrown as CodegenException.

Reason Message What to do
SnapshotUnreadable Snapshot {path} cannot be read.
SnapshotNotJson Snapshot {path} is not valid JSON: {reason}
NotASnapshot {path} is not a snapshot; write one with stewart generate --snapshot-out.
OutputDirectoryNotCreatable Cannot create output directory {path}.
FileNotWritable Cannot write {path}.
FileNotDeletable Cannot delete {path}.
UnmarkedFileInTheWay {path} was not written by stewart generate; move it out of the output directory.
ShrinkRefused Generating would delete {deleted} of {existing} generated files; run make generate ARGS=--allow-shrink if that is intended.
HomeAssistantNotRunning Home Assistant is {state}; run stewart generate again once it has started.

Thrown as CommandException.

Reason Message What to do
Rejected {reason}

Thrown as EventFireException.

Reason Message What to do
TypeInvalid An event type must be 1 to 64 characters, got “{eventType}”.
DataInvalid Data of event {eventType} at {path} is {actualType}; expected null, a finite scalar or an array of those.
DataNotKeyed Data of event {eventType} must be keyed by name, got a list.
Rejected Event {eventType} was rejected by Home Assistant: {detail}
Unreachable Event {eventType} could not reach Home Assistant: {detail}
TimedOut Event {eventType} timed out: {detail}
Overloaded Event {eventType} was refused: {detail}

Thrown as ExposureException.

Reason Message What to do
ComponentMissing Entity {key} cannot be exposed because the stewart Home Assistant integration is not installed; see stewart status. Install the Stewart integration in Home Assistant and restart it.
ProtocolMismatch Entity {key} cannot be exposed because the stewart Home Assistant integration speaks another protocol; see stewart status. Update Stewart or the integration so both have the same version.
ComponentRefused Entity {key} cannot be exposed because Home Assistant refused the stewart integration session; see stewart status.
OutsideApp Entity {key} cannot be exposed outside an app, because its unique ID needs an app ID.
KeyInvalid Exposed entity key “{key}” is invalid; use up to {limit} lowercase letters, digits and “_”, starting with a letter.
KeyTaken App {appId} already exposes an entity with key {key}.
ConfigInvalid Home Assistant cannot use this entity configuration: {detail}
StateInvalid Home Assistant cannot use this entity state: {detail}
Removed Exposed entity {key} was removed; expose it again to use it.
Unreachable Could not reach the broker for exposed entity {key}: {detail}
TimedOut The broker did not answer for exposed entity {key} within {timeout}.

Thrown as HistoryException.

Reason Message What to do
WindowInvalid A history window must end after it starts, got {startsAt} to {endsAt}.
RecorderUnavailable History of {entityId} is unavailable because Home Assistant has no history integration loaded.
Rejected History of {entityId} was rejected by Home Assistant: {detail}
Unreachable History of {entityId} could not reach Home Assistant: {detail}
TimedOut History of {entityId} timed out: {detail}
Overloaded History of {entityId} was refused: {detail}

Thrown as IdentifierException.

Reason Message What to do
EntityIdInvalid Entity ID “{entityId}” is invalid; expected “.<object_id>” in lowercase letters, digits and “_”.
AppIdInvalid App ID “{appId}” is invalid; use lowercase letters, digits, “-” and “_”, starting with a letter.
AppIdTooLong App ID “{appId}” is longer than {limit} characters.
EntityNotGenerated Entity ID “{entityId}” is not a generated {domain} entity; run stewart generate if it is new. Fix the ID, or run make generate for a new entity.
RegistryIdEmpty {kind} ID must not be empty.

Thrown as MqttException.

Reason Message What to do
TopicInvalid MQTT topic {topic} must be non-empty UTF-8, free of +, # and NUL characters.
PayloadUnencodable MQTT payload for {topic} cannot be encoded as JSON: {cause}
PayloadNotJson MQTT payload on {topic} is not valid JSON: {cause}
NotConfigured MQTT is not configured; set mqtt.url in stewart.yaml or STEWART_MQTT__URL in .env. Set STEWART_MQTT__URL.
PublishFailed Could not hand the MQTT message for {topic} to the broker: {cause}

Thrown as RegistryEditException.

Reason Message What to do
NotFound Entity {entityId} is not in the Home Assistant entity registry.
NothingToUpdate The registry update for {entityId} changes nothing.
Rejected Home Assistant rejected the registry update for {entityId}: {detail}
Unreachable The registry update for {entityId} could not reach Home Assistant: {detail}
TimedOut The registry update for {entityId} timed out: {detail}
Overloaded The registry update for {entityId} was refused: {detail}

Thrown as ScheduleException.

Reason Message What to do
CronInvalid Cron expression “{expression}” is invalid.
EntityTimeDomainUnsupported Entity “{entityId}” holds no time; expected an input_datetime or a sensor entity.
SunLocationUnknown A {event} schedule needs a location, and Home Assistant reported none. Set a location in Home Assistant under Settings → System → General, then restart Stewart.
TimeOfDayInvalid Time of day “{value}” is invalid; expected “HH:MM” or “HH:MM:SS”.
TimeOfDayOutOfRange Time of day {unit} {value} is outside 0-{highest}.
TimeWindowEmpty Time window starts and ends at {time}; start and end must differ.

Thrown as SelectorException.

Reason Message What to do
Empty {selectorKind} cannot be empty.
RegexInvalid Regular expression selector {pattern} is invalid: {compileError}.
MqttFilterInvalid MQTT topic filter {filter} must use + and # only as whole levels, with # last.

Thrown as ServiceCallException.

Reason Message What to do
Rejected {domain}.{service} was rejected by Home Assistant: {detail} Try the same call under Developer tools → Actions in Home Assistant.
Unreachable {domain}.{service} could not reach Home Assistant: {detail}
TimedOut {domain}.{service} timed out: {detail}
Overloaded {domain}.{service} was refused: {detail} Call fewer services at once, for example one call with several targets.
ActionNameInvalid “{action}” is not an action name; write it as domain.action, such as light.turn_on.

Thrown as StateException.

Reason Message What to do
EntityNotFound Entity “{entityId}” does not exist in Home Assistant.
StateChangedViaEvents The “{eventType}” event is only delivered through watchStateChanges(), not watchEvents().
ThresholdHysteresisNegative The hysteresis of {usage}() is {hysteresis}; it must be zero or more.
ThresholdNotFinite The threshold of {usage}() is {threshold}; it must be a finite number.
TransitionRuleConflict whenChangedTo() takes either from: or fromAnyState:, not both.

Thrown as StoreException.

Reason Message What to do
KeyInvalid Store key “{key}” is invalid; expected a letter or digit, then up to 127 letters, digits, “.”, “_”, “:” or “-”.
ValueNotEncodable The value for key “{key}” is not JSON-encodable: {cause}
ValueNotAMap {class}::toStorage() returned a list for key “{key}”; expected a map of named fields.
NotConfigured Persistence is not configured; set persistence.url in stewart.yaml or STEWART_PERSISTENCE__URL in .env. Set STEWART_PERSISTENCE__URL.
Unreachable The store at {target} is unreachable: {detail} Check STEWART_PERSISTENCE__URL and that Valkey runs: docker compose ps valkey.
Refused The store at {target} refused the operation: {detail}
TimedOut The store at {target} did not answer within {timeout}. Check that Valkey is not overloaded; docker compose logs valkey shows its state.
RecentlyFailed The store at {target} failed moments ago and is not being retried yet: {detail}
TypeMismatch Key “{key}” holds {actual}, not {expected}. Read the key with the type it was written with, or delete it.
NotIncrementable Key “{key}” cannot be incremented: {detail}
ValueNotIncrementable The stored value cannot be incremented: {detail}
ValueMalformed Key “{key}” does not hold valid JSON: {cause}
ValueRejected {class}::fromStorage() rejected the value of key “{key}” (handle the old shape or delete the key): {cause} Make fromStorage() accept the stored shape, or delete the key.

Thrown as SunException.

Reason Message What to do
CoordinateOutOfRange The {coordinate} {value} is outside -{limit} to {limit}.
LocationUnknown Home Assistant reported no location, so sun times are unknown.

Thrown as TimeException.

Reason Message What to do
DurationNegative Duration {microseconds}us is negative.
DurationNotFinite Duration {amount} is not a finite number.
DurationNotPositive The duration for {usage} must be at least 1ms.
DurationTooLarge Duration exceeds the 292-year maximum.
DurationUnparsable Duration “{text}” is invalid; expected a number and one unit, such as 500ms, 5s, 2m or 1h.
InstantUnparsable Instant “{text}” is not ISO-8601.

Thrown as TopicException.

Reason Message What to do
PayloadInvalid Topic payload at {path} is {actualType}; expected null, a finite scalar or an array of those.
PublishFailed Could not publish to topic {topic}: {cause}

Thrown as TriggerException.

Reason Message What to do
ConfigInvalid A Home Assistant trigger must be a map with a non-empty “trigger” or “platform” key.
ConfigUnencodable Trigger value at {path} cannot be encoded as JSON.
ListEmpty A trigger subscription needs at least one trigger.
TimeInvalid Trigger time “{time}” is neither HH:MM[:SS] nor an input_datetime or sensor entity.
TimePatternEmpty A time pattern trigger needs hours, minutes or seconds.
TemplateEmpty A template trigger needs a non-empty template.
ZoneInvalid Trigger zone “{zone}” is not a zone entity.
VariablesNotMap Trigger variables must be a map keyed by variable name.

Thrown as WaitException.

Reason Message What to do
Abandoned The wait ended before an event arrived, because its stream completed or its app stopped.

Thrown as MqttClientException.

Reason Message What to do
ConnectionRefused The MQTT server refused the connection: {refusal}.
ConnectionLost The MQTT connection was lost: {cause}
ConnectionClosed The MQTT connection is closed.
KeepaliveTimedOut The MQTT server left a keepalive ping unanswered for {keepalive}.
PacketMalformed The MQTT server sent a malformed {packetType} packet.
PacketUnexpected The MQTT server sent an unexpected {packetType} packet.
PacketIdsExhausted Every MQTT packet identifier is in flight.

Thrown as AppException.

Reason Message What to do
DuplicateId Automations {firstClass} and {secondClass} both use ID “{appId}”; “-” and “_” count as the same character.
DirectoryNotAutoloadable {directory} is not covered by a PSR-4 autoload rule. Add it to “autoload” in composer.json and run make install.
NotAnApp {className} has {attribute} but does not implement {appInterface}.
NotInstantiable {className} has {attribute} but cannot be instantiated.
IdInvalid {className}: automation ID “{appId}” is invalid; use lowercase letters, digits, “-” and “_”, starting with a letter.
InitializeTimedOut {appId} initialize() did not return within {timeout}. Raise supervision.initialize_timeout if needed.
Unknown No automation with ID “{appId}”.
Disabled Automation “{appId}” is not loaded because apps.{appId}.enabled is false.
TemplateAndAutomation {className} has both #[Automation] and #[AppTemplate]; keep #[Automation] to run it under its own ID, or #[AppTemplate] to run it only through apps..class.
InstanceClassMissing apps.{appId}.class names {className}, which does not exist.
InstanceClassNotMarked apps.{appId}.class names {className}, which has neither #[AppTemplate] nor #[Automation].
InstanceIdTaken apps.{appId}.class runs {className} under “{appId}”, which is already the ID of automation {takenBy}.
InstanceIdsCollide apps.{appId} and apps.{otherAppId} both name a class, and “-” and “_” count as the same character.

Thrown as BrokerException.

Reason Message What to do
NoWorkersStarted None of {workers} workers could be started. The log lines above name why each worker failed; stewart doctor checks the PHP setup.
WorkerSpawnTimedOut Worker {worker} did not connect within {timeout}.
ReloadConfigChanged {path} changed while the reload checked it, so nothing was applied; reload again.

Thrown as ConfigurationException.

Reason Message What to do
SchemaViolation {cause}
ConfigFileMissing Config file {path} not found. Fix the --config path; without the option, Stewart reads stewart.yaml in the project root, or the environment alone.
ConfigFileUnparsable Could not parse {path}: {cause} Fix the YAML where the cause points, then check the result with make config.
ConfigFileNotAMap {path} holds {actualType}; expected a map of settings.
ValueInvalid “{value}” is not {expected}.
KeyInvalid {path} must be {expected}.
KeyParseFailed {path} is invalid: {cause}
DurationTooShort {path} is {value}; expected at least {minimum}.
DurationTooLong {path} is {value}; expected at most {maximum}.
MaxDelayBelowInitialDelay {path} is {value}; expected at least {initialDelayPath} ({initialDelay}).
RestartWindowTooShort supervision.restart_window is {window}, but the backoff for restart_attempts ({attempts}) adds up to {backoffTotal}, so a crash-looping worker is never quarantined.
HomeAssistantMissing home_assistant is not configured. Set STEWART_HOME_ASSISTANT__URL and STEWART_HOME_ASSISTANT__TOKEN. make env creates .env with both keys to fill in.
PlaceholderUnset {setting} refers to ${{variable}}, which is not set. Set it or add a default: ${{variable}:-value}. Add the variable to .env.
ControlTokenMissing control.token is required while control.listen is set; set STEWART_CONTROL__TOKEN or set control.listen to “off”. make env writes a fresh token into a new .env; for an older one, add STEWART_CONTROL__TOKEN= followed by openssl rand -hex 16.
AdminTokenMissing http.admin.token is required while http.admin.listen is set; set STEWART_HTTP__ADMIN__TOKEN or set http.admin.listen to “off”. Create one with openssl rand -hex 16 and set it as STEWART_HTTP__ADMIN__TOKEN.
HttpListenAddressShared {setting} ({address}) and {otherSetting} ({otherAddress}) use the same port; give each HTTP listener a port of its own. Give one of them another port, such as tcp://0.0.0.0:9464 for http.metrics.listen.
WorkerIndexOutOfRange App “{appId}” is pinned to worker {worker}, but the highest worker index is {highestIndex}.
AppSelectionUnknown {option} names app “{appId}”, but no automation has that ID.
OptionsConflict {option} cannot be combined with {conflicting}.
EnvironmentVariableUnknown {variable} is not a Stewart setting; run stewart config:reference for all options. Fix the variable name; keys are joined by two underscores.
EnvironmentVariableTooDeep {variable} is deeper than the configuration; {valuePath} is a value, not a section.
EnvironmentVariableNamesSection {variable} names a section; set the keys under it, such as {variable}{separator}KEY.
EnvironmentValueInvalid {variable} is “{raw}”; expected {expected}. Fix the value in .env; make config-reference lists the type of every setting.
EnvironmentValueUnparsable {variable} is not a valid inline list or map: {cause}
EnvironmentVariableConflict {variable} and {otherVariable} both set {valuePath}; keep one.
EnvironmentSecretFileUnreadable {variable} points to {path}, which cannot be read.
MqttPackageMissing mqtt.url is set, but the MQTT client is not installed; run composer require stewart-php/mqtt. Or unset STEWART_MQTT__URL if no app uses MQTT.

Thrown as ContainerException.

Reason Message What to do
ServiceTypeMismatch Service “{serviceId}” is {actualType}; expected {expectedType}.
BuildFailed Worker {workerId} failed to build its container: {cause}
AppInServicesFile services.php defines “{serviceId}” as automation {class}. Configure it in stewart.yaml instead.
PeerStoreWritable {class}::__construct() takes ${parameter} with #[PeerStore] as Store; type it ReadableStore.
PeerStoreAppIdInvalid {class}::__construct() takes ${parameter} with #[PeerStore]: {cause}
MessageHandlerDuplicated {firstHandler} and {secondHandler} both handle {messageClass}.
AppOptionInvalid Option “{option}” of app “{appId}” is “{value}”, which is not a valid {expectedType}.
AppOptionUnknown App “{appId}” sets option “{option}”, which matches no parameter of {class}::__construct(). Rename the option in stewart.yaml to match a constructor parameter.
AppOptionMissing App “{appId}” needs option “{option}”; {class}::__construct() takes ${option} with no default. Set the option, or give the parameter a default.
AppOptionAmbiguous App “{appId}” sets option “{option}”, which matches parameters {parameters} of {class}::__construct(); use the exact name.
AppOptionRepeated App “{appId}” sets ${parameter} of {class}::__construct() twice, as options “{firstOption}” and “{secondOption}”.
AppBuildFailed App “{appId}” cannot be built: {cause}
AppClassMissing App “{appId}” names class {class}, which cannot be loaded.
ServicesFileMissing Services file {path} no longer exists.

Thrown as ControlException.

Reason Message What to do
SocketInUse Another daemon is already listening on {address}. Stop it or change control.listen.
SocketUnremovable Could not remove the stale control socket {path}.
SocketDirectoryNotCreatable Could not create control socket directory {directory}.
SocketPathNotASocket The control socket path {path} exists and is not a socket.
FrameNotJson Frame is not JSON: {cause}
FrameNotAnObject Frame is {actualType}; expected a JSON object.
FrameUnencodable Could not encode {frameClass}: {cause}
FrameClassUnknown Unknown frame {frameClass}.
FrameUndecodable Could not decode the “{frameType}” control frame: {cause}
ConfigUnavailable Could not read control.listen and control.token (pass –address and –token): {cause}
ControlDisabled The configuration sets control.listen to off; pass –address. Pass the daemon’s address, such as --address tcp://homeassistant.local:7333, or run make status next to the daemon.
TokenMissing No control token; set STEWART_CONTROL__TOKEN or pass –token.
ConnectionRejected The broker refused the connection: {reason} For a wrong token, use the daemon’s STEWART_CONTROL__TOKEN; make status runs next to the daemon with its .env.
UnexpectedGreeting The broker sent {frameClass} instead of a welcome.
ProtocolMismatch Broker speaks control protocol {brokerProtocol}, this client {clientProtocol}. Use the stewart of the running daemon’s version, for example make status, which runs in its container.
BrokerHungUp The broker hung up before the session ended.
UnexpectedFrame The broker sent {frameClass} instead of {expectedFrameClass}.
RequestTimedOut The broker did not answer within {timeout}.
RequestUnexpected The control session expects a request, not {frameClass}.
RequestFailed The broker refused the request: {message}
RequestUnanswerable The broker could not answer the request: {cause}

Thrown as DeployException.

Reason Message What to do
CommitIdInvalid “{value}” is not a commit hash.
CommandTimedOut {command} did not finish within {timeout}.
CommandFailed {command} failed with exit code {exitCode}: {detail}
PrepareOutputUnexpected release-prepare printed “{output}” instead of a commit and its state.

Thrown as ScaffoldException.

Reason Message What to do
ClassNameInvalid “{name}” is not a class name; use letters and digits starting with a capital, such as PorchLight.
FileExists {path} already exists. Choose another class name, or remove the existing file and its test first.
IdTaken An automation already has the ID “{appId}”. Pass another ID with --id, or rename the class.

Thrown as TransportException.

Reason Message What to do
Closed Transport is closed.
SendFailed Failed to send to peer: {cause}
Unencodable Could not encode {messageClass} for IPC: {cause}
PeerDisconnected Peer disconnected: {cause}
ReceiveFailed Failed to receive from peer: {cause}
UnexpectedMessage Expected a JSON frame, got {actualType}.
UndecodableFrame Could not decode “{messageType}” frame: {reason}
ProtocolMismatch Broker speaks IPC protocol {actual}, this worker {expected}. Restart the daemon after updating Stewart, so the broker and its workers run the same version.
MessageTypeMissing No IPC message type is registered for {messageClass}.
MessageTagMissing IPC message {messageClass} has no #[IpcMessage] attribute.
MessageTagDuplicated IPC tag “{tag}” is declared by both {firstClass} and {secondClass}.

Thrown as WorkerException.

Reason Message What to do
Unknown No worker “{workerId}”; stewart status lists the workers.
ShuttingDown Stewart is shutting down, so worker {workerId} is not restarted.

Thrown as StoreSetupException.

Reason Message What to do
DsnInvalid Store URL is invalid ({length} characters); expected a URL like “redis://host:6379/0”. Fix STEWART_PERSISTENCE__URL; URL-encode special characters in a password.
SchemeUnsupported No installed store backend handles “{scheme}://”; installed schemes: {supported}.
UnknownPeerApp No automation has ID “{appId}”, so its store cannot be opened.
PrefixInvalid Store prefix “{prefix}” is invalid; expected a letter or digit, then up to 63 letters, digits, “.”, “_” or “-”.

Thrown as JsonShapeException.

Reason Message What to do
WrongType “{key}” is {actualType}; expected {expected}.
Missing “{key}” is missing; expected {expected}.
UnexpectedValue “{key}” is {value}; expected {expected}.
InstantInvalid “{key}” is invalid: {cause}
ObjectRefused “{key}” was refused: {cause}

Thrown as HarnessException.

Reason Message What to do
AlreadyStarted The harness already runs {class}; use one harness per app.
AutomationMissing {class} has neither #[Automation] nor #[AppTemplate], so it is not an app.
TemplateNeedsAppId {class} is an #[AppTemplate]; pass the ID of the instance to test as startApp(appId: ‘…’).
NotStarted No app is running; call startApp() first.
OptionMissing {class}::__construct() needs option “{option}”; pass it to startApp().
OptionUnknown Option “{option}” matches no parameter of {class}::__construct().
OptionAmbiguous Option “{option}” matches parameters {parameters} of {class}::__construct(); use the exact name.
OptionRepeated {class}::__construct() gets ${parameter} twice, as options “{firstOption}” and “{secondOption}”.
ParameterUnresolvable {class}::__construct() takes ${parameter} as {type}, which the harness has no fake for; pass one with provideService().