How Does the TeslaMate API Module Handle Rate Limiting and Unauthorized Errors?

The TeslaMate API module handles rate limiting by returning structured error tuples with retry-after headers, while unauthorized errors trigger a circuit-breaker pattern using the :fuse library to prevent repeated authentication attempts.

The TeslaMate.Api module in the teslamate-org/teslamate repository serves as the primary interface to the official Tesla API, encapsulating all HTTP interactions and providing resilient error handling for two critical failure modes: rate limiting (429 Too Many Requests) and unauthorized access (401 Unauthorized).

Circuit Breaker Architecture for Unauthorized Errors

When the Tesla API returns an unauthorized response, the module does not immediately retry the request or refresh the token blindly. Instead, it implements a circuit breaker using the Erlang :fuse library to protect against cascading failures from repeated bad authentication attempts.

Fuse Configuration in lib/teslamate/api.ex

During GenServer initialization, the init/1 function installs a fuse specific to the API instance using a standard policy of 5 failures within 10 minutes. If the threshold is exceeded, the fuse remains blown for approximately 9,999 hours until manually reset.


# In lib/teslamate/api.ex

:fuse.install(
  :"#{name}.unauthorized",
  {{:standard, 5, :timer.minutes(10)}, {:reset, :timer.hours(9999)}}
)

This configuration ensures that after five consecutive authentication failures, the system stops attempting to contact the Tesla API and treats subsequent requests as signed out.

Unauthorized Error Processing in handle_result/3

The handle_result/3 function pattern matches on %TeslaApi.Error{reason: :unauthorized} and executes a three-step recovery process:

  1. Melt the fuse – Increment the failure counter via :fuse.melt/1
  2. Query fuse status – Check if the circuit is :blown or still :ok
  3. Conditional response – Either delete the stored auth token or trigger a token refresh

# From lib/teslamate/api.ex (handle_result/3)

{:error, %TeslaApi.Error{reason: :unauthorized}} ->
  :ok = :fuse.melt(:"#{name}.unauthorized")

  case :fuse.ask(:"#{name}.unauthorized", :sync) do
    :blown ->
      # Too many failures: sign out completely

      true = :ets.delete(name, :auth)
      {:error, :not_signed_in}

    :ok ->
      # Still within threshold: attempt token refresh

      send(name, :refresh_auth)
      {:error, :unauthorized}
  end

If the fuse is blown, the module clears the ETS table entry storing the authentication credentials, forcing the user to re-authenticate. If intact, it dispatches a :refresh_auth message to asynchronously renew the access token via TeslaApi.Auth.refresh/1.

Rate Limit Detection and Response

When the Tesla API enforces rate limiting, it returns a 429 status code with a retry-after header. The handle_result/3 function extracts this value and returns a three-tuple error containing the specific atoms :too_many_request and the numeric retry duration.


# From lib/teslamate/api.ex (handle_result/3)

{:error, %TeslaApi.Error{reason: :too_many_request, message: retry_after}} ->
  Logger.warning("TeslaApi.Error / :too_many_request #{retry_after}")
  {:error, :too_many_request, retry_after}

This structured response allows calling processes to implement intelligent backoff strategies, sleeping for the exact duration specified by the API before retrying.

Practical Error Handling Examples

Handling Rate Limits in Application Code

When calling TeslaMate.Api.list_vehicles/0 or similar functions, pattern match on the error tuples to implement appropriate retry logic:

case TeslaMate.Api.list_vehicles() do
  {:ok, vehicles} ->
    process_vehicles(vehicles)

  {:error, :too_many_request, retry_after} ->
    :timer.sleep(retry_after * 1000)
    retry_request()

  {:error, :unauthorized} ->
    IO.puts("Authentication expired, attempting refresh...")

  {:error, :not_signed_in} ->
    IO.puts("Please authenticate to continue")
end

Monitoring Fuse State for Debugging

You can inspect the circuit breaker state directly using the :fuse module to determine if the API is currently rejecting requests due to repeated authentication failures:

case :fuse.ask(:"TeslaMate.Api.unauthorized", :sync) do
  :ok -> IO.puts("Circuit closed: API calls allowed")
  :blown -> IO.puts("Circuit open: Authentication locked out")
end

Key Implementation Files

  • lib/teslamate/api.ex – Contains the handle_result/3 function and fuse management logic for rate limiting and unauthorized error handling
  • lib/tesla_api/error.ex – Defines the %TeslaApi.Error{} struct used for pattern matching error reasons
  • lib/tesla_api/auth.ex – Implements Auth.refresh/1 invoked when the circuit breaker permits token renewal
  • test/teslamate/api_test.exs – Test suite validating the unauthorized and rate-limiting error paths

Summary

  • Unauthorized errors trigger a circuit breaker in handle_result/3 that melts after repeated failures and either signs the user out (if blown) or initiates token refresh
  • Rate limiting returns {:error, :too_many_request, retry_after} with the exact seconds to wait, enabling precise backoff implementation
  • The :fuse library provides the circuit breaker mechanism with a 5-failure threshold over 10 minutes, installed during init/1
  • All error handling logic resides in lib/teslamate/api.ex, leveraging ETS for credential storage and GenServer messaging for asynchronous token refresh

Frequently Asked Questions

How does TeslaMate prevent infinite authentication loops when the Tesla API returns 401 errors?

The TeslaMate.Api module uses the :fuse library to create a circuit breaker that melts on each unauthorized response. After five melts within ten minutes, the fuse blows, causing the module to delete the stored authentication data and return {:error, :not_signed_in} rather than continuing to retry with invalid credentials.

What should I do when receiving a {:error, :too_many_request, retry_after} response?

Extract the retry_after value from the error tuple and implement a delay before retrying your request. The integer represents seconds, so multiply by 1000 when using :timer.sleep/1 or schedule a Process send_after for asynchronous handling.

Where is the authentication token actually refreshed when an unauthorized error occurs?

When the fuse is still intact (less than five recent failures), handle_result/3 sends a :refresh_auth message to the GenServer, which triggers TeslaApi.Auth.refresh/1 in lib/tesla_api/auth.ex to obtain a new access token using the stored refresh token.

Can I manually reset the circuit breaker if I fix authentication issues externally?

Yes, since the fuse is registered with the name :"TeslaMate.Api.unauthorized", you can call :fuse.reset(:"TeslaMate.Api.unauthorized") from an IEx session or administrative function to immediately close the circuit and allow API requests to resume.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →