{"record":{"id":"605dda39453349e1","repo":"gastownhall/beads","slug":"get-dependents-scan-w","errorCode":null,"errorMessage":"get dependents: scan: %w","messagePattern":"get dependents: scan: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependencies.go","lineNumber":1179,"sourceCode":"//nolint:gosec // G201: table names come from WispTableRouting (hardcoded constants)\nfunc GetDependentsWithMetadataInTx(ctx context.Context, tx DBTX, issueID string) ([]*types.IssueWithDependencyMetadata, error) {\n\ttype depMeta struct {\n\t\tdepID, depType string\n\t}\n\n\t// Query both dependency tables to find all dependents.\n\tvar deps []depMeta\n\tfor _, depTable := range []string{\"dependencies\", \"wisp_dependencies\"} {\n\t\trows, err := tx.QueryContext(ctx, fmt.Sprintf(\n\t\t\t`SELECT issue_id, type FROM %s WHERE %s = ?`, depTable, DepTargetExpr), issueID)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"get dependents from %s: %w\", depTable, err)\n\t\t}\n\t\tfor rows.Next() {\n\t\t\tvar d depMeta\n\t\t\tif scanErr := rows.Scan(&d.depID, &d.depType); scanErr != nil {\n\t\t\t\t_ = rows.Close()\n\t\t\t\treturn nil, fmt.Errorf(\"get dependents: scan: %w\", scanErr)\n\t\t\t}\n\t\t\tdeps = append(deps, d)\n\t\t}\n\t\t_ = rows.Close()\n\t\tif err := rows.Err(); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"get dependents: rows from %s: %w\", depTable, err)\n\t\t}\n\t}\n\n\tif len(deps) == 0 {\n\t\treturn nil, nil\n\t}\n\n\t// Fetch all dependent issues.\n\tids := make([]string, len(deps))\n\tfor i, d := range deps {\n\t\tids[i] = d.depID\n\t}","sourceCodeStart":1161,"sourceCodeEnd":1197,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependencies.go#L1161-L1197","documentation":"This wraps rows.Scan failing while reading a dependent row (issue_id, type) in GetDependentsWithMetadataInTx. Scan errors mean the column values couldn't be decoded into the destination strings — usually a NULL in a NOT NULL-assumed column or an unexpected type.","triggerScenarios":"GetDependentsWithMetadataInTx iterates rows from dependencies/wisp_dependencies and a row has NULL issue_id or type, or a column type the driver cannot convert to string.","commonSituations":"Rows inserted by older versions or external tools with NULL type values; schema altered to nullable after the code assumed NOT NULL; using a driver with strict type conversion (e.g. refusing to scan non-string types into string).","solutions":["Find the offending row: SELECT issue_id, type FROM <named table> WHERE issue_id IS NULL OR type IS NULL.","Backfill/repair NULL or malformed rows, then add a NOT NULL constraint if appropriate.","If NULLs are expected, they must be fixed at the data layer — this code scans into plain string and cannot accept them.","Confirm the driver/schema column types match (VARCHAR/TEXT, not BLOB or int)."],"exampleFix":"// before: nullable columns break Scan(&d.depID, &d.depType)\n// after: clean data and enforce NOT NULL\nUPDATE dependencies SET type = 'blocks' WHERE type IS NULL;\nALTER TABLE dependencies MODIFY type VARCHAR(32) NOT NULL;","handlingStrategy":"validation","validationCode":"// Detect rows that would fail Scan before calling the API\nrows, err := tx.QueryContext(ctx, `SELECT issue_id, type FROM dependencies WHERE issue_id IS NULL OR type IS NULL\n    UNION ALL SELECT issue_id, type FROM wisp_dependencies WHERE issue_id IS NULL OR type IS NULL`)\nif err != nil { return err }\ndefer rows.Close()\nif rows.Next() { return errors.New(\"NULL dependency rows found; clean data before querying\") }","typeGuard":"func isScanErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"scan\")\n}","tryCatchPattern":"deps, err := GetDependentsWithMetadataInTx(ctx, tx, issueID)\nif err != nil {\n    if strings.Contains(err.Error(), \"scan\") {\n        return fmt.Errorf(\"data integrity problem in dependency table: %w\", err)\n    }\n    return err\n}","preventionTips":["Enforce NOT NULL on issue_id and type columns in both dependency tables.","Validate data after imports or external writes.","Add DB-level checks to reject NULL dependency rows at insert time.","Audit for schema drift after upgrades."],"tags":["database","sql","go","scan-error"],"backgroundTag":"sql-scan-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}