Skip to content

Troubleshooting

Symptom: You just signed in, but API calls return 401 Token expired or 401 Invalid token.

Causes:

  • Clock skew. JWT validation checks exp (expiration) against the server’s clock. If your device’s clock is more than a few minutes off, tokens may appear expired. Sync your system clock with NTP.
  • Token copied incorrectly. Make sure you’re using the full access_token string with no trailing whitespace or line breaks.
  • Wrong header format. The header must be exactly Authorization: Bearer <access_token> — note the space after Bearer and no quotes around the token value.
  • Using the wrong token. access_token goes in the Authorization header. refresh_token is only for exchanging at $SUPABASE_URL/auth/v1/token?grant_type=refresh_token.

Fix: Verify your system clock is accurate (date -u should match UTC within a few seconds). If the clock is fine, refresh your access token (see Authentication) and confirm the Authorization header is formatted correctly.


Symptom: API calls were working, now they return 401 Token expired.

Cause: Supabase access tokens last 1 hour. Your client needs to exchange the refresh token for a new access token before the current one expires.

Fix: Exchange your refresh token for a new access token:

Terminal window
curl -X POST https://api.hungrymachines.io/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "YOUR_REFRESH_TOKEN"}'

The response contains a new access_token and a new refresh_token — save both, then retry your original request with the new access token. If this call itself returns 400 Invalid Refresh Token, the refresh token is expired (30-day max) — the user needs to log in again.


Symptom: POST /api/v1/readings returns 201 accepted, but GET /api/v1/schedule returns source: "defaults" with a flat comfort band and zero savings.

Cause: The optimizer needs about 3 days of continuous readings (72 hourly buckets, with no more than a couple of gap-filled hours) to build your first per-user thermal model. Until the model is fit, the schedule endpoint returns safe defaults based on your preferences and setpoint_temps is null.

What to expect:

  1. Days 1–2: source: "defaults", flat comfort band, model_confidence: null, setpoint_temps: null.
  2. Day 3+: Once you have ~72 hourly buckets of data, the daily initial-fit job (06:30 UTC) builds your first per-user model. The nightly slice that follows produces your first source: "optimization" schedule.
  3. Sunday 02:00 UTC weekly refit: All active users get a refreshed model from the last 14 days of readings.

Fix: Keep pushing readings consistently. The defaults are safe to apply — they maintain your comfort settings but won’t save energy. When source is "defaults", apply the band midpoint as your setpoint: (high_temps[idx] + low_temps[idx]) / 2. Once the model is ready, setpoint_temps becomes a 48-element array and optimized schedules appear automatically.


Symptom: source is "optimization" but the high_temps and low_temps arrays have little variation — they look almost flat.

Causes:

  • Savings level 1 (tight). With a ±2°F band, the optimizer has very little room to shift loads. Try increasing to savings level 2 (±6°F) or 3 (±12°F) via PUT /api/v1/preferences.
  • Flat rate plan. If your utility charges the same rate all day, there’s no price signal to optimize against. The optimizer works best with time-of-use (TOU) or real-time pricing.
  • Mild weather. When outdoor and indoor temps are close, the HVAC runs very little and there’s less to optimize.

Fix: Check your savings_level and optimization_mode in preferences. If you’re on a flat-rate plan, the optimizer may not produce significant schedule variation — that’s expected behavior, not a bug. You can inspect (and override) the rate curve the optimizer is using via GET / PUT /api/v1/rates — submitting your own 24-hour curve is the fastest way to give the optimizer a price signal to work with.


Symptom: POST /api/v1/appliances returns 403 Upgrade required.

Cause: Free-tier accounts are limited to one HVAC appliance. You’ll get a 403 if you try to:

  • Register a second appliance (any type)
  • Register a non-HVAC device (EV charger, home battery, water heater)

Fix: Upgrade to Premium via the dashboard at hungrymachines.io/dashboard. Premium accounts can register unlimited appliances of any type.


Symptom: POST /api/v1/readings returns 429 Rate limit exceeded: max 300 readings per user per day.

Cause: Your client is pushing more than 300 readings in a 24-hour window. At 5-minute intervals, a single sensor generates 288 readings/day — within the limit. But batching catch-up readings after an outage or running multiple sensors without coordination can push you over.

Fix: Implement client-side rate tracking. If you have multiple sensors, coordinate their reporting windows. If you’re batching after an outage, spread the catch-up over multiple days or trim to the most recent 300 readings.


If none of the above resolves your issue: