Troubleshooting
This page organizes common problems by symptom. Click through to the relevant section for detailed guidance.
Can't connect to Home Assistant
- Token issues: Verify
HASSETTE__TOKENis set correctly in your.envfile. See Authentication. - Connection refused / timeout: Check
base_urlinhassette.toml. If running in Docker, ensure Hassette can reach Home Assistant's network. See Docker Troubleshooting.
Apps not loading
- App not discovered: Verify
apps.directorypoints to the correct directory and your app file is registered inhassette.toml. See Application Configuration. Success: you'll seeINFO hassette.<AppName>.0 ... ─ App initializedin the logs. - Import errors: Check for missing dependencies or syntax errors in logs. See Docker Troubleshooting.
-
App precheck fails: If an app fails to load, Hassette won't start by default. The precheck runs each app's module through import before starting the WebSocket connection, so any problem is reported immediately. Common causes and their log signatures:
- Syntax error or bad import — a
SyntaxErrororModuleNotFoundErrorat the top of your app file. Look for:ERROR hassette.utils.app_utils — Failed to load app 'MyApp': SyntaxError: invalid syntax (at /apps/my_app.py:12) - Class not found — the
class_nameinhassette.tomldoesn't match the actual class name in the file. Look for:AttributeError: Class MyApp not found in module apps.my_app - Invalid configuration — a required
AppConfigfield has no value and no default. Look for:ERROR ... Failed to load app 'MyApp' due to bad configuration
To diagnose without blocking startup, set
allow_startup_if_app_precheck_fails = trueinhassette.tomltemporarily. This logs the same errors but lets other apps run. Remove it once the problem is fixed — a failing precheck means the broken app won't be loaded either way. - Syntax error or bad import — a
Event handler never runs
- Entity ID typo: Double-check the entity ID string —
"binary_sensor.motion"vs"binary_sensor.motoin". Hassette won't error on a non-existent entity; the handler simply never fires. changed_totype mismatch: Home Assistant state values are strings.changed_to="on"works;changed_to=Truedoes not — it compares against the Pythonbool, not the HA string"on".- Domain excluded: Check
bus_excluded_domainsandbus_excluded_entitiesin yourhassette.toml— events from excluded domains are silently dropped before reaching your handlers. - App not enabled: Verify the app's config block has
enabled = true(the default). A disabled app's handlers are never registered. - Attribute-only change: By default,
on_state_changeonly fires when the main state value changes. If only an attribute changed (e.g., brightness), passchanged=False. See Filtering — ThechangedParameter.
Home Assistant goes offline
When Home Assistant becomes unreachable or disconnects mid-session, Hassette handles recovery automatically without restarting the process.
What happens immediately:
- The WebSocket receive loop detects the disconnect (closed frame, connection reset, or server disconnect) and raises internally.
- Hassette fires a
hassette.event.websocket_disconnectedevent on the bus — your apps can subscribe to it viaawait self.bus.on_websocket_disconnected(handler=..., name="ws_disconnect")to react (for example, to pause outgoing calls). - The
WebsocketServiceis marked not-ready and the framework begins reconnecting.
Reconnection sequence:
The initial connection itself retries up to 5 times with exponential backoff (starting at 1 second, capping at 32 seconds) before the service is considered failed. If the service fails, the ServiceWatcher takes over:
- It restarts
WebsocketServiceusing itsRestartSpec: up to 5 restarts within a 300-second sliding window (TRANSIENT type). - Each restart waits an exponentially increasing delay starting at 2 seconds, doubling each attempt, capped at 60 seconds.
- When Home Assistant comes back and the service recovers to RUNNING and becomes ready, Hassette fires
hassette.event.websocket_connectedand the restart budget resets automatically.
If the restart budget is exhausted, WebsocketService enters EXHAUSTED_COOLING for a 300-second cooldown, then resets its budget and retries. The TRANSIENT restart type means it keeps trying rather than shutting down immediately.
What to look for in logs:
WARNING hassette.WebsocketService -- Retrying _inner_connect in Xs as it raised CouldNotFindHomeAssistantError: ...
ERROR hassette.WebsocketService -- Serve() task failed: CouldNotFindHomeAssistantError ...
INFO hassette.ServiceWatcher -- Service 'WebsocketService' restarting (attempt N, waiting Xs)
DEBUG hassette.WebsocketService -- Connected to WebSocket at ws://...
INFO hassette.ServiceWatcher -- Service 'WebsocketService' in cooldown for 300.0s (cycle 1)
During reconnection, your app code keeps running — the bus, scheduler, and state manager remain active. .call_service() will raise ResourceNotReadyError while the WebSocket is down because it depends on an active connection. .get_state() returns the last-known cached value — the data may be stale, but reads do not fail. Call is_ready() on the state proxy to check whether data is fresh. The cache is replaced with live data once the WebSocket reconnects. Your handlers registered via the bus will resume receiving events as soon as the WebSocket reconnects; no re-registration is needed.
Event handler exceptions
Exceptions raised inside a bus handler are caught by the framework, logged, and swallowed — they do not propagate to the caller, do not crash the app, and do not affect other handlers.
The specific behavior:
- The exception is logged at
ERRORlevel with the full traceback:Handler error (topic=..., handler=...) \n<traceback> - Hassette records the invocation in the telemetry database with
status='error'and the error type and message. - The app continues running normally; subsequent events are dispatched as usual.
This matches the scheduler's behavior for jobs — exceptions fail silently (logged to error).
What to look for in logs:
ERROR hassette.CommandExecutor -- Handler error (topic=hass.event.state_changed.light.kitchen, handler=Listener<Hassette.MyApp.0 - on_light_change>)
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute 'brightness'
See also: Writing Handlers for how to use dependency injection to avoid common handler errors.
Scheduler not firing
- Job scheduled for the past:
run_once(at="07:00")called after 7 AM defers the job to tomorrow (with a WARNING log).run_daily(at="07:00")fires at the next 7 AM occurrence (today if before 7 AM, tomorrow otherwise). - Runs too often or too rarely:
run_every(seconds=5)is 5 seconds, not minutes — userun_every(minutes=5)for a 5-minute interval. Forrun_cron, the expression"5 * * * *"means "at minute 5 of every hour", not "every 5 minutes" — use"*/5 * * * *"for intervals. - Exception in task: Unhandled exceptions in scheduled tasks are logged at ERROR level but don't crash the scheduler. Check your logs.
- See Job Management — Troubleshooting for more.
Database degraded / telemetry missing
- Stats strip shows zeroed-out metrics: The telemetry database may be unavailable. Check for disk space issues or file permission problems.
- Docker: Check the data volume has space:
docker compose exec hassette df -h /data. The database file is at/data/hassette.dbby default. - Safe to delete: Deleting
hassette.dbonly loses telemetry history — your automations continue to run. Restart Hassette to recreate the database. - See Database & Telemetry — Degraded Mode for details.
Cache not persisting
- Data lost after restart: Verify the
data_diris correctly configured and writable. In Docker, ensure the/datavolume is mounted. - Cache shared between instances: All instances of the same app class share one cache directory. Use
self.app_config.instance_nameas a key prefix to avoid collisions. - See App Cache — Troubleshooting for more.
Custom state class not registering
- Ensure the class has
domain: Literal["your_domain"]as a field. - If overriding
__init_subclass__, callsuper().__init_subclass__(). - See Custom States — Troubleshooting.
Upgrading Hassette
Check your current version:
hassette --version # if installed as a CLI tool
uv pip show hassette # shows installed version in your project
Upgrade to the latest release:
uv add hassette@latest
Release notes: See the Changelog for what changed in each version. Breaking changes are flagged with a BREAKING CHANGE footer in commit messages and are called out explicitly in the changelog under a "Breaking Changes" heading.
Major version upgrades — data directory: On bare-metal installs (not Docker), Hassette's default data_dir and config_dir include the major version number (e.g., ~/.local/share/hassette/v0/). If a future major release changes this to v1/, Hassette will start with a fresh data directory. To keep your existing telemetry and cache, set data_dir and config_dir explicitly in hassette.toml or via the HASSETTE__DATA_DIR / HASSETTE__CONFIG_DIR environment variables. Docker users are unaffected — the /data and /config paths are version-independent.
Docker-specific issues
For container startup failures, dependency installation problems, health check failures, hot reload issues, and performance tuning, see the dedicated Docker Troubleshooting guide.
Web UI not accessible
- Running locally: Open
http://localhost:8126/ui/after starting Hassette. - Running in Docker: Ensure your
docker-compose.ymlincludesports: ["8126:8126"]. - Disabled: Check that
runandrun_uiare bothtrueunder[hassette.web_api]inhassette.toml. - See Web UI for configuration options.