multica-ai/multica · error

update Codex task_usage batch: %w

Error message

update Codex task_usage batch: %w

What it means

Returned when one UPDATE batch of the correction loop fails during the --execute phase. The query recomputes eligible rows per batch (same predicate as the dry-run), sets input_tokens = GREATEST(input_tokens - cache_read_tokens, 0), and returns counts. Because each batch commits independently and the predicate excludes already-corrected rows, earlier batches stay applied; re-running resumes rather than double-applying.

Source

Thrown at server/cmd/backfill_codex_usage_cache/main.go:292

     ORDER BY COALESCE(tu.updated_at, tu.created_at), tu.id
     LIMIT $3
     FOR UPDATE OF tu SKIP LOCKED
),
updated AS (
    UPDATE task_usage tu
       SET input_tokens = GREATEST(tu.input_tokens - tu.cache_read_tokens, 0),
           updated_at = now()
      FROM candidates c
     WHERE tu.id = c.id
     RETURNING c.removed_tokens
)
SELECT COUNT(*)::bigint, COALESCE(SUM(removed_tokens), 0)::bigint FROM updated`

	var totalRows, totalRemoved int64
	for {
		var rows, removed int64
		if err := pool.QueryRow(ctx, query, cfg.cutoff, cfg.workspaceID, cfg.batchSize).Scan(&rows, &removed); err != nil {
			return totalRows, totalRemoved, fmt.Errorf("update Codex task_usage batch: %w", err)
		}
		if rows == 0 {
			break
		}
		totalRows += rows
		totalRemoved += removed
		slog.Info("updated Codex task_usage batch", "rows", rows, "input_tokens_removed", removed, "total_rows", totalRows)
		if cfg.sleepBetweenBatches > 0 {
			select {
			case <-time.After(cfg.sleepBetweenBatches):
			case <-ctx.Done():
				return totalRows, totalRemoved, ctx.Err()
			}
		}
	}
	return totalRows, totalRemoved, nil
}

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Re-run the tool with the same --cutoff — completed batches no longer match the eligibility predicate, so it resumes from where it stopped.
  2. Address the wrapped cause: raise statement/lock timeouts for the session, or run during lower write traffic.
  3. Use --sleep-between-batches to reduce contention if lock conflicts recur.

Example fix

# before
./backfill_codex_usage_cache --cutoff ... --execute
# update Codex task_usage batch: ... lock timeout

# after: idempotent resume, gentler pacing
./backfill_codex_usage_cache --cutoff 2026-06-01T12:00:00Z --execute --sleep-between-batches 500ms
Defensive patterns

Strategy: retry

Prevention

When it happens

Trigger: A dead connection, statement timeout, or lock conflict on task_usage mid-loop; ctx cancelled (SIGINT) during the update; a concurrent writer altering rows the UPDATE targets.

Common situations: Long backfills on busy production tables hitting lock_timeout/statement_timeout; postgres failover mid-run; operators interrupting the --execute phase.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/ff422e6861611d40. Report an issue: GitHub.