goose format: statement line containing "Down" is treated as the Down boundary, truncating the statement
Summary
When reading a migration directory with format=goose, any statement line containing the case-sensitive token Down — e.g. product copy like 'Download report' inside a VALUES row — is treated as the -- +goose Down boundary. The line is dropped from the Up statement, which then fails to parse on apply/lint.
Repro (atlas v1.3.3-d7c803a-canary, also v1.3.3-140afb4 and v1.2.4)
20260903000001_demo.sql:
-- +goose Up
CREATE TABLE items (id serial PRIMARY KEY, label text NOT NULL);
INSERT INTO items (label)
VALUES ('Download report')
ON CONFLICT DO NOTHING;
-- +goose Down
DROP TABLE items;$ atlas migrate hash --dir "file://.?format=goose"
$ atlas migrate apply --dir "file://.?format=goose" --url "postgres://postgres:pass@localhost:5499/demo?sslmode=disable"
...
Error: sql/migrate: executing statement "INSERT INTO items (label)\nON CONFLICT DO NOTHING;" from version "20260903000001": pq: syntax error at or near "ON" at position 2:1 (42601)The executed statement shows the VALUES line was swallowed: INSERT INTO items (label)\nON CONFLICT DO NOTHING;.
Characterization
Same file, only the label changed:
| label | result |
|---|---|
'Download report' |
truncated → 42601 |
'Downtime alert' |
truncated → 42601 |
'markdown guide' |
applies cleanly |
'shut down now' |
applies cleanly |
So the boundary match appears to be a case-sensitive substring/token match for Down on arbitrary statement lines, rather than requiring the -- +goose Down directive comment.
Expected
Only a -- +goose Down annotation line should end the Up section (goose itself applies this file correctly; so does golang-migrate on the equivalent split files).
Impact
Any seed/data migration whose content contains words like "Download" or "Downtime" fails migrate lint/migrate apply (and migrate push replay) with a false 42601, with no config escape we could find. We currently work around it with continue-on-error on the lint step in CI.
Source: ariga/atlas