# How the TeslaMate Repair Module Identifies and Fixes Data Inconsistencies

> Discover how the TeslaMate Repair module automatically identifies and fixes common data inconsistencies like missing addresses. Learn how it geocodes your Tesla data efficiently and accurately.

- Repository: [TeslaMate/teslamate](https://github.com/teslamate-org/teslamate)
- Tags: internals
- Published: 2026-06-23

---

**The TeslaMate Repair module is a background GenServer that hourly scans the database for Drive and ChargingProcess records missing address associations, then geocodes their positions to fill the gaps using a circuit-breaker pattern.**

TeslaMate, the open-source Tesla data logger hosted at `teslamate-org/teslamate`, occasionally encounters incomplete records where address information is missing due to transient geocoding failures or legacy imports. The `TeslaMate.Repair` module automatically reconciles these data inconsistencies by linking orphaned drives and charging sessions to valid geographic addresses without manual intervention.

## Detecting Missing Address Records

The repair process begins with targeted Ecto queries in [`lib/teslamate/repair.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/repair.ex) that identify records with incomplete location data while ensuring positional data exists for geocoding.

### Identifying Incomplete Drive Records

The module queries for all `Drive` entries where either `start_address_id` or `end_address_id` is `nil`, but both start and end positions are present. This condition indicates a drive was logged without successful reverse-geocoding of its endpoints.

```elixir

# Query logic from lib/teslamate/repair.ex (lines 41-57)

from d in Drive,
  where: is_nil(d.start_address_id) or is_nil(d.end_address_id),
  where: not is_nil(d.start_position_id) and not is_nil(d.end_position_id),
  preload: [:start_position, :end_position]

```

### Identifying Incomplete Charging Processes

Similarly, the module identifies `ChargingProcess` entries where `address_id` is `nil` despite having an associated position record.

```elixir

# Query logic from lib/teslamate/repair.ex (lines 61-68)

from c in ChargingProcess,
  where: is_nil(c.address_id),
  where: not is_nil(c.position_id),
  preload: [:position]

```

## The Repair Execution Loop

Once problematic records are identified, the private `repair/1` function processes them recursively, updating the database with correct address associations.

### Recursive Record Processing

The function walks through the result list, logging each repair attempt and handling individual failures without aborting the entire batch.

```elixir

# Simplified recursive structure from lib/teslamate/repair.ex

defp repair([]), do: :ok
defp repair([entity | rest]) do
  # Process entity...

  repair(rest)
end

```

### Changeset Updates and Persistence

For each entity, the module builds an Ecto changeset using `Drive.changeset/2` or `ChargingProcess.changeset/2`, populates the missing `*_address_id` fields, and persists the update via `Repo.update/1`.

```elixir

# Repairing a Drive record with missing addresses

drive
|> Drive.changeset(%{
     start_address_id: get_address_id(drive.start_position),
     end_address_id:   get_address_id(drive.end_position)
   })
|> Repo.update()

```

Any database errors during this phase are logged but do not stop the repair loop, ensuring subsequent records still process.

## Geocoding with Circuit-Breaker Protection

The `get_address_id/1` function implements defensive programming to prevent overwhelming the geocoding service during repair operations.

### Rate Limiting and Fuse Pattern

Before calling the geocoder, the function checks a circuit-breaker named `:addr_fuse`. If the fuse is open, the lookup is skipped. If closed, the process sleeps for 1.5 seconds to respect rate limits, then proceeds.

```elixir

# From lib/teslamate/repair.ex (lines 119-124)

case :fuse.ask(:addr_fuse, :sync) do
  :ok ->
    Process.sleep(1_500)  # Rate limiting

    Locations.find_address(position)
  # ...

end

```

On repeated failures, the module melts the fuse via `:fuse.melt/1`, which automatically reinstalls after a cooldown period, preventing endless API calls during outages.

### Address Lookup and Creation

The `Locations.find_address/1` function (defined in [`lib/teslamate/locations.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/locations.ex)) performs the reverse geocoding. It either returns an existing `Address` record or creates a new one based on the latitude and longitude from the position data.

## Manual Repair Triggers

While the GenServer schedules automatic repairs once per hour by default, you can initiate an immediate run manually using the public API:

```elixir

# Trigger repair from IEx console

TeslaMate.Repair.trigger_run()

```

This is useful after bulk imports or when you notice specific data gaps that need immediate attention.

## Summary

- **TeslaMate.Repair** is a GenServer in [`lib/teslamate/repair.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/repair.ex) that runs hourly to fix missing address associations in the database.
- It detects inconsistencies by querying for `Drive` records with nil `start_address_id`/`end_address_id` and `ChargingProcess` records with nil `address_id`, provided positional data exists.
- The module uses a recursive `repair/1` function to update records via Ecto changesets without stopping on individual errors.
- Geocoding is protected by a circuit-breaker (`:addr_fuse`) with 1.5-second rate limiting to prevent API abuse.
- Manual repairs can be triggered via `TeslaMate.Repair.trigger_run/0` from the application console.

## Frequently Asked Questions

### How often does TeslaMate automatically repair data inconsistencies?

By default, the Repair module schedules a repair cycle once per hour using `Process.send_after/3`. It also triggers an immediate run when the GenServer initializes via `trigger_run/0`, ensuring that recently imported or updated records are processed quickly after application startup.

### What happens if the geocoding service is unavailable during repairs?

The module implements a circuit-breaker pattern using the Fuse library. If geocoding requests fail repeatedly, the fuse melts and blocks further calls until it automatically reinstalls. This prevents the system from hammering a failing API and allows the external service to recover before retrying.

### Can I repair specific drives or charging sessions individually?

The public API only exposes `trigger_run/0`, which scans for all incomplete records across both tables. To target specific records, you would need to interact directly with the `TeslaMate.Log.Drive` or `TeslaMate.Log.ChargingProcess` schemas and the `TeslaMate.Locations` module in an IEx session.

### Why do some drives show missing start or end addresses?

Missing addresses typically result from transient geocoding failures during the original data import, network timeouts when the drive was logged, or legacy data migrated from earlier TeslaMate versions. The Repair module exists specifically to backfill these gaps automatically without requiring database manual edits.