---
title: "Reconcile Index Jobs"
method: POST
path: "/v1/internal/index-jobs/reconcile"
---

# Reconcile Index Jobs

`POST /v1/internal/index-jobs/reconcile`

## Request

- Base URL: `https://api.graphify.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.graphify.com/v1/internal/index-jobs/reconcile`
- Auth: none declared

Reap stranded index builds to ``failed`` AND drain the pending index outbox.

Two independent recoveries share this one tick, mirroring ``reconcile_fv_runs`` /
``reconcile_review_runs``:

* The REAP (pure DB, ``BuildRecoverySweeper``) runs on EVERY tick regardless of
  whether indexing is currently configured -- it takes the OPTIONAL indexer so a
  build stranded before a mid-flight disable is still terminalised (a required
  indexer would 503 before the reap). An index worker that dies mid-run (e.g. OOM
  on a large repo) never posts a terminal callback, so its build sits in a
  non-terminal status with a merely-EXPIRED lease -- untouched by the callback
  projector (which rejects an expired lease) and invisible to the outbox drain
  (which only re-drives ``pending`` rows; a stranded build is already
  ``enqueued``). The reap marks every such build ``failed`` so a lost worker
  SURFACES instead of stranding forever, and emits ``stranded_inprogress`` when it
  reaps anything.
* The DRAIN re-drives ``outbox_status="pending"`` builds whose SQS publish failed
  transiently. It no-ops (zeros) when indexing is unconfigured, so this tick is
  safe on every deployment.
* The stuck-queued RECOVERY (pure DB, ``StuckQueuedBuildRecovery``; DARK behind
  ``stuck_queued_recovery_enabled``) remediates the class the reap cannot reach and
  the gauge only counts: dispatched (``enqueued``) but NEVER-leased ``queued`` rows
  whose SQS message was dead-lettered by a capacity burst. It runs BETWEEN reap and
  drain so a row it flips back to ``pending`` is republished later this same tick.
  Flag off => not constructed at all; the tick is byte-identical to before.
* The FROZEN-PENDING gauge (``detect_frozen_pending_builds``, detection only) runs
  AFTER the drain and pages on the one class nothing above can see: a build whose
  FIRST publish was lost (``enqueue_attempts=0``), still ``pending``, with no gate
  explaining the wait. The drain is what increments attempts, so such a row can never
  reach the thrasher ceiling, and the coalescer used to fold every later push into it
  -- a repo frozen with no build and no error (2026-09-04).

## Request body

- ReconcileRequest
  - `limit` integer

## Response `200`

Successful Response

- unknown

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/graphify/apis/graphify-control-plane.md) · [All operations](https://skmtc.dev/graphify/apis/graphify-control-plane/llms.txt) · [OpenAPI document](https://skmtc.dev/graphify/apis/graphify-control-plane/revisions/0e75c82b5ea8?raw)
