multica-ai/multica · error
task_usage_hourly pre-103 hook: %w
Error message
task_usage_hourly pre-103 hook: %w
What it means
The pre-migration hook that backfills task_usage_hourly data before migration 103 (via taskusagebackfill.Hook) returned an error, aborting the migration run before schema_migrations is updated. The hook wraps the same slice/rollup machinery as the standalone backfill command, so its failure causes mirror errors 184–187: missing tables/functions, cancelled context, lock contention, or timeouts.
Source
Thrown at server/cmd/migrate/main.go:198
return fmt.Errorf("relation %q exists but is not an index", indexRegclass)
}
if isValid {
return nil
}
qualifiedName := pgx.Identifier{schemaName, relationName}.Sanitize()
if _, err := pool.Exec(ctx, "DROP INDEX CONCURRENTLY IF EXISTS "+qualifiedName); err != nil {
return fmt.Errorf("drop invalid concurrent index %s: %w", qualifiedName, err)
}
slog.Warn("removed invalid index before migration retry", "index", qualifiedName)
return nil
}
}
func runTaskUsageHourlyHook(ctx context.Context, pool *pgxpool.Pool) error {
res, err := taskusagebackfill.Hook(ctx, pool, taskusagebackfill.HookOptions{})
if err != nil {
return fmt.Errorf("task_usage_hourly pre-103 hook: %w", err)
}
if res.Skipped != "" {
slog.Info("task_usage hourly rollup hook: skipped",
"reason", res.Skipped,
"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-bandView on GitHub (pinned to 2c0912b6ec)
Solutions
- Pre-warm the data with the standalone tool first: run server/cmd/backfill_task_usage_hourly (it coordinates via advisory lock 4246), then re-run migrate
- Raise statement_timeout/lock_timeout for the migration session
- Read the wrapped error to pinpoint the failing slice/query; re-running is safe because hook failures leave the version unrecorded
Example fix
# before migrate up # pre-103 hook times out on large dataset # after backfill_task_usage_hourly -db $DSN # backfills + stamps watermark migrate up # hook now skips/completes fast
Defensive patterns
Strategy: retry
Try / catch
res, err := taskusagebackfill.Hook(ctx, pool, taskusagebackfill.HookOptions{})
if err != nil {
return fmt.Errorf("task_usage_hourly pre-103 hook: %w", err)
} Prevention
- Pre-run the standalone backfill tool on large datasets before upgrading
- Raise statement timeouts for `migrate up` on production
- Hook failures leave the version unrecorded, so re-running is always safe
When it happens
Trigger: task_usage exists but task_usage_hourly schema objects are missing or half-applied; a huge task_usage table making the in-migration backfill exceed statement_timeout; concurrent cron rollup contending on row locks; connection drop mid-hook.
Common situations: Self-hosted upgrade with months of usage data — the hook runs inline during `migrate up` and takes long enough to hit timeouts or get killed.
Related errors
- attribution strict-constraint pre-198 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/0d706a38bfc7f617.
Report an issue: GitHub.