{"record":{"id":"dbf572dc9ad54716","repo":"gastownhall/beads","slug":"legacy-sqlite-schema-drift-in-s-hidden-column","errorCode":null,"errorMessage":"legacy SQLite schema drift in %s hidden column","messagePattern":"legacy SQLite schema drift in (.+?) hidden column","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/migration/legacysqlite/reader.go","lineNumber":343,"sourceCode":"}\n\nfunc verifyTable(ctx context.Context, db *sql.Tx, table, want string) error {\n\trows, err := db.QueryContext(ctx, \"PRAGMA table_xinfo(\"+table+\")\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer rows.Close()\n\tvar got []string\n\tfor rows.Next() {\n\t\tvar cid, hidden int\n\t\tvar name, typ string\n\t\tvar notNull, pk int\n\t\tvar defaultValue sql.NullString\n\t\tif err := rows.Scan(&cid, &name, &typ, &notNull, &defaultValue, &pk, &hidden); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif hidden != 0 {\n\t\t\treturn fmt.Errorf(\"legacy SQLite schema drift in %s hidden column\", table)\n\t\t}\n\t\tdefaultText := \"-\"\n\t\tif defaultValue.Valid {\n\t\t\tdefaultText = defaultValue.String\n\t\t}\n\t\tgot = append(got, fmt.Sprintf(\"%s|%s|%d|%s|%d\", name, typ, notNull, defaultText, pk))\n\t}\n\tif err := rows.Err(); err != nil {\n\t\treturn err\n\t}\n\tif strings.Join(got, \" \") != want {\n\t\treturn fmt.Errorf(\"legacy SQLite schema drift in %s\", table)\n\t}\n\treturn nil\n}\n\nfunc verifyFKs(ctx context.Context, db *sql.Tx, table string) error {\n\trows, err := db.QueryContext(ctx, \"PRAGMA foreign_key_list(\"+table+\")\")","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/migration/legacysqlite/reader.go#L325-L361","documentation":"verifyTable reads PRAGMA table_xinfo for each table and rejects any column whose 'hidden' flag is non-zero. Non-zero hidden values indicate generated, virtual, or internal (e.g. STRICT/virtual-table shadow) columns that are not part of the audited legacy layout — evidence the table shape deviates from the exact contract this package migrates. The error names the offending table.","triggerScenarios":"Export -> read -> verify -> verifyTable: a row from PRAGMA table_xinfo(<table>) has hidden != 0 for any of metadata, issues, dependencies, labels, comments. Occurs when the table was created as a virtual table, has generated columns, or was modified by tooling.","commonSituations":"The database was produced or altered by a different tool or schema generator that added generated columns; a virtual-table-based shim replaced a real table; custom patches to the legacy schema.","solutions":["Compare the table definition: sqlite3 beads.db \".schema issues\" and remove/revert generated or virtual columns so the layout matches the documented legacy schema","Restore the original database from a backup taken before the schema was modified","Recreate the table with the canonical legacy schema and copy rows into it, then retry the export","Check with the bd project which release produced the file; an unexpected producer may have introduced drift"],"exampleFix":"-- inspect for generated columns\nsqlite3 beads.db \"PRAGMA table_xinfo(issues);\"  -- last column (hidden) must be 0 for every row\n-- fix: rebuild the table without the generated column\nsqlite3 beads.db \"CREATE TABLE issues_new (...same legacy columns...); INSERT INTO issues_new SELECT <cols> FROM issues; DROP TABLE issues; ALTER TABLE issues_new RENAME TO issues;\"","handlingStrategy":"validation","validationCode":"// pre-flight: ensure no hidden/generated columns in any legacy table\nfunc checkNoHiddenColumns(dbPath string, tables []string) error {\n\tdb, err := sql.Open(\"sqlite3\", dbPath+\"?mode=ro\"); if err != nil { return err }\n\tdefer db.Close()\n\tfor _, t := range tables {\n\t\trows, err := db.Query(\"PRAGMA table_xinfo(\" + t + \")\"); if err != nil { return err }\n\t\tfor rows.Next() {\n\t\t\tvar cid, hidden int; var name, typ string; var nn, pk int; var d sql.NullString\n\t\t\tif err := rows.Scan(&cid, &name, &typ, &nn, &d, &pk, &hidden); err != nil { rows.Close(); return err }\n\t\t\tif hidden != 0 { rows.Close(); return fmt.Errorf(\"%s: hidden column %s\", t, name) }\n\t\t}\n\t\trows.Close()\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"if err := legacysqlite.Export(ctx, src, out, os.Stdout); err != nil {\n\tif strings.Contains(err.Error(), \"hidden column\") {\n\t\treturn fmt.Errorf(\"database has generated/virtual columns; restore original schema or rebuild the table before export\")\n\t}\n\treturn err\n}","preventionTips":["Never add generated or virtual columns to legacy databases you intend to migrate","Only let the supported bd release create/modify the schema","Keep a pre-modification backup of the legacy database","Audit .schema output against the documented legacy layout before migrating"],"tags":["sqlite","migration","schema","drift"],"backgroundTag":"schema-drift","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}