{"record":{"id":"a6f01ab6763fbac4","repo":"gastownhall/beads","slug":"scan-s-constraint-violation-w","errorCode":null,"errorMessage":"scan %s constraint violation: %w","messagePattern":"scan (.+?) constraint violation: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/versioncontrolops/mergesettle.go","lineNumber":937,"sourceCode":"\n// violationsAreIssueFKOnly reports whether every constraint violation recorded\n// for table is a foreign-key violation referencing issues — the only class the\n// cascade repair understands. violation_info is Dolt's JSON descriptor; its\n// ReferencedTable names the FK's parent.\nfunc violationsAreIssueFKOnly(ctx context.Context, db DBConn, table string) (bool, error) {\n\t// table is from the fixed fkCascadeRepairDeletes allowlist, never user input.\n\t//nolint:gosec // G202: hardcoded table name.\n\trows, err := db.QueryContext(ctx,\n\t\t\"SELECT violation_type, violation_info FROM dolt_constraint_violations_\"+table)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"query %s constraint violations: %w\", table, err)\n\t}\n\tdefer rows.Close()\n\tfor rows.Next() {\n\t\tvar vtype string\n\t\tvar vinfo any\n\t\tif err := rows.Scan(&vtype, &vinfo); err != nil {\n\t\t\treturn false, fmt.Errorf(\"scan %s constraint violation: %w\", table, err)\n\t\t}\n\t\tif vtype != \"foreign key\" {\n\t\t\treturn false, nil\n\t\t}\n\t\t// Server mode returns violation_info as JSON text; the embedded engine\n\t\t// hands back the driver's native value (e.g. merge.FkCVMeta), which\n\t\t// marshals to the same JSON.\n\t\tvar infoJSON []byte\n\t\tswitch v := vinfo.(type) {\n\t\tcase []byte:\n\t\t\tinfoJSON = v\n\t\tcase string:\n\t\t\tinfoJSON = []byte(v)\n\t\tdefault:\n\t\t\tb, err := json.Marshal(v)\n\t\t\tif err != nil {\n\t\t\t\treturn false, nil // unknown descriptor shape — operator decides\n\t\t\t}","sourceCodeStart":919,"sourceCodeEnd":955,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/versioncontrolops/mergesettle.go#L919-L955","documentation":"While inspecting per-table violation rows, a row could not be scanned into (violation_type string, violation_info any). This means the row shape or types deviate from what the two-destination Scan expects. The check aborts with this wrapped error, blocking the auto-repair (which requires proving all violations are FK-only).","triggerScenarios":"A violations row returns a violation_type that is not string-convertible or an unexpected number of columns (engine version changed violation_info encoding), causing rows.Scan to fail.","commonSituations":"Dolt upgrade changing violation_info from JSON text to a structured native driver value (or vice versa); server vs embedded engine returning different driver value types; corrupt violation metadata.","solutions":["Check the wrapped %w error for column count vs type-conversion details.","Run SELECT violation_type, violation_info FROM dolt_constraint_violations_<table> manually to inspect actual shapes.","Upgrade beads (or the Dolt engine) so the driver's violation_info encoding matches the code path (JSON text vs native value).","Until fixed, resolve violations manually rather than via auto-repair."],"exampleFix":"// before\nvar vtype string\nvar vinfo any\nif err := rows.Scan(&vtype, &vinfo); err != nil {\n    return false, fmt.Errorf(\"scan %s constraint violation: %w\", table, err)\n}\n// after: accept either JSON text or native value shapes\nvar vtype string\nvar vinfo any\nif err := rows.Scan(&vtype, &vinfo); err != nil {\n    var raw []byte\n    if serr := rows.Scan(&vtype, &raw); serr != nil {\n        return false, fmt.Errorf(\"scan %s constraint violation: %w\", table, err)\n    }\n    vinfo = string(raw)\n}","handlingStrategy":"type-guard","validationCode":"rows, _ := db.Query(\"SELECT violation_type, violation_info FROM dolt_constraint_violations_issues LIMIT 1\")\ncols, _ := rows.Columns()\n// proceed only when len(cols) == 2","typeGuard":"func violationRowShapeMatches(ctx context.Context, db DBConn, table string) bool {\n    rows, err := db.QueryContext(ctx, \"SELECT violation_type, violation_info FROM dolt_constraint_violations_\"+table+\" LIMIT 1\")\n    if err != nil {\n        return false\n    }\n    defer rows.Close()\n    c, _ := rows.Columns()\n    return len(c) == 2\n}","tryCatchPattern":"ok, err := violationsAreIssueFKOnly(ctx, db, t)\nif err != nil && strings.Contains(err.Error(), \"scan\") {\n    // violation_info encoding drift: fall back to manual resolution\n    return manualFKResolution(t)\n}","preventionTips":["Check rows.Columns() shape before scanning system tables","Handle both JSON-text and native violation_info encodings","Test pulls across engine version upgrades"],"tags":["dolt","sql-scan","constraint-violations"],"backgroundTag":"sql-scan-column-mismatch","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}