multica-ai/multica · error
attribution strict-constraint pre-198 hook: %w
Error message
attribution strict-constraint pre-198 hook: %w
What it means
The pre-198 hook that backfills accountable_user_id from originator_user_id (attributionbackfill.Hook, GH #5544 / MUL-4897) failed, aborting the run before the strict attribution constraint migration executes. Without this hook, self-hosted databases that never ran the out-of-band backfill would fail migration 198's validation outright.
Source
Thrown at server/cmd/migrate/main.go:221
"watermark_stamped", res.WatermarkStamped)
return nil
}
slog.Info("task_usage hourly rollup hook: backfill complete",
"slices", res.SlicesProcessed,
"rows_touched", res.RowsTouched,
"from", res.From.Format("2006-01-02T15:04:05Z07:00"),
"to", res.To.Format("2006-01-02T15:04:05Z07:00"))
return nil
}
// runAttributionStrictHook backfills accountable_user_id from
// originator_user_id before migration 198 validates the strict attribution
// constraint, so self-hosted upgrades that never ran the out-of-band
// backfill recover automatically (GH #5544 / MUL-4897).
func runAttributionStrictHook(ctx context.Context, pool *pgxpool.Pool) error {
res, err := attributionbackfill.Hook(ctx, pool, attributionbackfill.HookOptions{})
if err != nil {
return fmt.Errorf("attribution strict-constraint pre-198 hook: %w", err)
}
slog.Info("attribution backfill hook: complete",
"rows_backfilled", res.RowsBackfilled,
"batches", res.Batches,
"mismatch_normalized", res.MismatchNormalized)
return nil
}
// migrationAdvisoryLockKey is the int64 identifier used with Postgres
// pg_advisory_lock to serialize the migration loop across concurrent
// runners (multi-replica backend Deployment, scale-up, or a manual
// `migrate up` overlapping with pod startup). The exact value is
// arbitrary — it just needs to be stable across every process that runs
// migrations against the same database. See GitHub multica-ai/multica#3647.
const migrationAdvisoryLockKey int64 = 7244554146635925501
// defaultSchemaMigrationsTable is the unqualified name of the bookkeeping
// table that tracks which migrations have been applied. Tests overrideView on GitHub (pinned to 2c0912b6ec)
Solutions
- Inspect the wrapped error: 42P01/42703 mean schema drift — apply/repair earlier migrations first
- For timeouts on large tables, raise statement_timeout and ensure no long transactions hold locks on tasks
- Re-run `migrate up` — the hook is batched and resumable, and the version is only recorded after success
Defensive patterns
Strategy: retry
Try / catch
res, err := attributionbackfill.Hook(ctx, pool, attributionbackfill.HookOptions{})
if err != nil {
return fmt.Errorf("attribution strict-constraint pre-198 hook: %w", err)
} Prevention
- Keep schema drift out of upgraded databases — apply migrations in order
- Clear long transactions on tasks before upgrading past 198
- Re-run `migrate up` after fixing the wrapped cause; the hook resumes
When it happens
Trigger: The tasks/attribution tables or columns referenced by the backfill query are missing or have unexpected types; statement/lock timeouts while batch-updating a large tasks table; connection loss mid-batch; cancelled context.
Common situations: Upgrading an old self-hosted instance with many rows where accountable_user_id is NULL; a partially applied earlier migration leaving schema drift.
Related errors
- task_usage_hourly pre-103 hook: %w
- rollup slice %s..%s: %w
- stamp watermark: %w
- inspect concurrent index %q: %w
- relation %q exists but is not an index
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/31515487108834a3.
Report an issue: GitHub.