Troubleshooting
Real failure modes, the actual text Goalfeed produces, and the next concrete step — not a generic "check your configuration."
Every event attempt (success or failure) is written to the JSONL app log and
queryable at /api/logs whether or not Home Assistant is reachable
— that log is where every error below actually shows up. Start there:
curl -s "http://localhost:8080/api/logs?limit=5" | python3 -m json.tool
# or, without --web, read the file directly:
tail -n 5 app.log.jsonl
"home assistant url is empty"
Where you'll see it: an app log entry with
"target": "ha:event:goal", "success": false, and
"error": "refusing to send Home Assistant request: home assistant url is
empty" (this is real, captured output — not a paraphrase). No exception,
no crash — Goalfeed keeps polling and logging, it just can't reach Home
Assistant yet.
Cause: neither home_assistant.url/
home_assistant.access_token in config.yaml (or the matching
GOALFEED_HOME_ASSISTANT_* env vars) nor the Supervisor add-on env vars
(SUPERVISOR_API/SUPERVISOR_TOKEN) are set.
Next step: set both keys in config.yaml (see the
configuration reference) if you're not running as
the Home Assistant add-on, or confirm the add-on's Supervisor integration is enabled
if you are. Then check /api/homeassistant/status.
"does not look like a private/local address"
Where you'll see it: the same app log target, with an error like
refusing to send Home Assistant request: home assistant url "http://ha.example.com"
does not look like a private/local address; set home_assistant.allow_remote_url:
true if you intentionally run a remote Home Assistant instance (real error
text from utils/hurl.go).
Cause: home_assistant.url resolves to a public
hostname or routable IP. Goalfeed refuses this by default so a poisoned or
hand-edited config can't send your long-lived access token to an attacker-controlled
host — see Security for the full reasoning.
Next step: if this is genuinely a remote Home Assistant instance
you control, set home_assistant.allow_remote_url: true. If it isn't
— if you didn't expect a public hostname here at all — treat this as the
check working as intended and fix the URL instead.
"Failed to send event to Home Assistant" / a non-2xx status in the log
Where you'll see it: an app log entry with
"success": false and an "error" field containing an HTTP
status like 401 Unauthorized or 404 Not Found, rather than
the URL-validation message above (so the URL passed validation, but the request
itself failed).
Cause: usually an expired or wrong long-lived access token
(401), or a URL that's reachable but wrong (404 if it
doesn't point at a real Home Assistant instance's REST API).
Next step: generate a fresh long-lived access token from your
Home Assistant profile page, update home_assistant.access_token, and
restart Goalfeed. If you're on the add-on, this shouldn't happen at all — the
Supervisor token doesn't expire the way a manually-created one can; check
/api/homeassistant/status instead.
The web UI shows a bare JSON status blob instead of the scoreboard
Where you'll see it: opening http://<host>:8080/
returns
{"api":"/api","message":"Goalfeed API Server","status":"running","ws":"/ws","note":"Frontend
not available. Install Node.js and npm to enable the web interface."} (real,
captured output) instead of the React scoreboard.
Cause: the web/frontend/build directory doesn't exist
next to the binary. This happens if you built with make backend instead
of make build, or ran a Go binary built from a bare
go build without the frontend step (the plain Dockerfile
always produces this — see Install). The REST API
and WebSocket feed still work in this mode; only the bundled UI is missing.
Next step: use a release tarball from
GitHub Releases
(bundles the frontend), or run make build / make frontend
from a source checkout with Node 20+ installed.
"I set --nfl WPG and got a flag error"
Cause: --nfl isn't a real flag. The only registered
CLI flags are --nhl, --mlb, and --cfl. NFL,
IIHF, and Olympic Men's/Women's Hockey team lists are YAML/env-only
(watch.nfl, GOALFEED_WATCH_NFL, etc.).
Next step: set those via config.yaml or a
GOALFEED_WATCH_* environment variable instead — see the full
table in the configuration reference.
Config changes don't seem to take effect, and there's no error at all
Cause: Goalfeed looks for ./config.yaml in the
current working directory only — there is no --config
flag to point it elsewhere. If it isn't found, the config read fails silently (the
error is discarded internally) and every key falls back to its zero value: no
watched teams, no Home Assistant URL, headless mode. Nothing crashes; you just get
an idle process.
Next step: confirm you're launching Goalfeed from the same
directory as config.yaml (systemd/Supervisor users: check the
service's WorkingDirectory). If in doubt, set at least one option via
CLI flag or GOALFEED_* env var instead — those don't depend on the
working directory.
POST /api/refresh doesn't do anything
Cause: it's a stub left over from a refactor. It returns
HTTP 200 and logs "Refresh active games - placeholder
implementation", but doesn't actually refresh anything.
Next step: don't rely on it. Active games refresh automatically
every minute (checkLeaguesForActiveGames); if you need it sooner,
restart the process.
Home Assistant config changes I made through the web UI disappeared on restart
Cause: by default, POST /api/homeassistant/config
only updates the running process's in-memory config for the current session
— it does not write config.yaml to disk unless you've opted in.
The response message says so explicitly:
"Configuration updated for this session only (not persisted to disk). Set
web.allow_config_writes: true in config.yaml to allow this API to write
config.yaml."
Next step: edit config.yaml directly for changes that
should survive a restart, or set web.allow_config_writes: true if you
specifically want the runtime API to persist config to disk — see
Security for why this defaults to off.
IIHF games show up in the API but no goal ever fires
Cause: this is expected, not a bug. IIHF has a full client and
service and answers /api/games/history, /api/upcoming, and
/api/teams, but it is never registered in the live polling loop, so no
Home Assistant event fires for it. See the leagues
reference for the full tier breakdown.
Still stuck?
Open an issue with the exact log line and your config.yaml (redact the
access token) at
github.com/goalfeed/goalfeed/issues
— that's the only supported contact channel.