Conversation
psql -f ran without -v ON_ERROR_STOP=1, so a migration with a failing statement would print the error, keep running later statements in the same file, and still exit 0 -- letting the migration get recorded as applied. Every psql call now goes through an ON_ERROR_STOP=1 wrapper, and the one pipe that could mask a psql exit code (the "already applied?" check, via | tr -d ' ') is gone in favor of -t -A. Also closes the CREATE INDEX CONCURRENTLY IF NOT EXISTS retry trap: that clause matches by name only, so an index left INVALID by an interrupted concurrent build is silently accepted as already-there on a retried deploy, with no error for ON_ERROR_STOP to catch. After a migration applies, the runner now checks pg_index.indisvalid for any index it builds CONCURRENTLY before recording success -- protecting idx_request_logs_created_at (LuthienResearch#811) from shipping invalid. Adds a 12-test integration suite pinning both behaviors against a real Postgres, and wires it into CI (scoped to this file -- the broader integration marker has pre-existing unrelated failures that have never run in CI, a separate concern flagged but not fixed here). PR LuthienResearch#812
legion-implementer
Bot
force-pushed
the
fix/migration-runner-error-stop
branch
from
August 23, 2026 17:19
1d1dd48 to
fad0a20
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
docker/run-migrations.shrunspsql -f "$migration"without-v ON_ERROR_STOP=1. psql's default behavior is to print a failing statement's error, keep executing the rest of the script, and still exit 0 — so a migration whose statements partially failed still gets recorded as applied in_migrations. Verified locally: a migration file withCREATE TABLE; INSERT; <bad statement>; INSERT;runs to completion, both INSERTs commit, and the script exits 0.This specifically endangers #811's
idx_request_logs_created_atindex migration:CREATE INDEX CONCURRENTLY IF NOT EXISTSmatches an existing index by name only. If a concurrent build is ever interrupted (dropped connection, OOM, crashed backend — not a SQL error), Postgres leaves anINVALIDindex under that name. A retried deploy'sIF NOT EXISTSthen finds the name already taken, prints a NOTICE, and reports success with exit 0 —ON_ERROR_STOPcan't catch this because psql never sees an error on that run. Reproduced locally by killing the backend mid-build, then re-running the sameCREATE INDEX CONCURRENTLY IF NOT EXISTSstatement: it reports success while the index stays permanently invalid.Fix
psqlinvocation now goes through arun_psql()wrapper that adds-v ON_ERROR_STOP=1.psql -t <<EOF | tr -d ' ', which discardspsql's exit status behindtr's (always 0). Replaced withpsql -t -Aso there's no pipe to mask it.CREATE INDEX CONCURRENTLYstatements (comment-stripped, so a comment merely mentioning the phrase isn't misparsed) and checkspg_index.indisvalidfor each named index before recording the migration as applied. An invalid index now hard-fails the run instead of silently "succeeding."set -o pipefail: this repo's CI runs the script directly via its#!/bin/shshebang onubuntu-latest, where/bin/shisdash, which doesn't supportpipefail(confirmed locally — it's a fatal "Illegal option" underset -e). The Alpine (ash) image this also ships in does support it, but the fix had to work in both, so the one masking pipe was removed instead.Tests
Added
tests/luthien_proxy/integration_tests/test_migration_runner.py— 12 tests against a real Postgres (each in its own scratch DB), covering: aborting on a failing statement (and not recording it, and not running later statements in the same file or later files), the CONCURRENTLY/indisvalidretry trap (planted deterministically via a duplicate-data unique-index build, no timing races), no false positives on a clean CONCURRENTLY build or on comments mentioning the phrase, the pre-existing drift checks (missing-file / hash-mismatch), skip-if-already-applied, and a full run of the realmigrations/postgres/set.Wired into CI in
.github/workflows/dev-checks.yaml, scoped to this one file rather than the wholeintegrationmarker: that marker is currently excluded from every workflow (pyproject.toml's default addopts filters it out, and no workflow passes-m integration), so the pre-existingintegration_tests/suite has never actually run in CI — confirmed locally, wheretest_migration_sync.py::test_schemas_matchfails against currentmainright now. That's a separate, pre-existing gap outside this PR's scope; flagging it here rather than silently expanding the diff to fix unrelated tests.Related
created_atindex migration this protects)