{"record":{"id":"1e2e37457b89e048","repo":"gastownhall/beads","slug":"failed-to-scan-stale-issue-id-w","errorCode":null,"errorMessage":"failed to scan stale issue id: %w","messagePattern":"failed to scan stale issue id: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/stale.go","lineNumber":77,"sourceCode":"\n\tif filter.Limit > 0 {\n\t\tquery += fmt.Sprintf(\" LIMIT %d\", filter.Limit)\n\t}\n\n\trows, err := tx.QueryContext(ctx, query, args...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get stale issues: %w\", err)\n\t}\n\n\t// Collect IDs first, then batch-fetch full issues.\n\t// Close rows explicitly before the nested fetch — MySQL/Dolt drivers\n\t// can't handle multiple active result sets on one connection.\n\tvar ids []string\n\tfor rows.Next() {\n\t\tvar id string\n\t\tif err := rows.Scan(&id); err != nil {\n\t\t\trows.Close()\n\t\t\treturn nil, fmt.Errorf(\"failed to scan stale issue id: %w\", err)\n\t\t}\n\t\tids = append(ids, id)\n\t}\n\tif err := rows.Err(); err != nil {\n\t\trows.Close()\n\t\treturn nil, fmt.Errorf(\"stale issues rows: %w\", err)\n\t}\n\trows.Close()\n\n\tif len(ids) == 0 {\n\t\treturn nil, nil\n\t}\n\n\t// GetIssuesByIDsInTx returns issues in arbitrary order (WHERE IN),\n\t// so re-order to preserve the updated_at ASC ordering from the query.\n\tissues, err := GetIssuesByIDsInTx(ctx, tx, ids, nil)\n\tif err != nil {\n\t\treturn nil, err","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/stale.go#L59-L95","documentation":"GetStaleIssuesInTx scans a single string (the issue id) from each row of the stale-issues SELECT; this error wraps a rows.Scan failure. With a literal single-column 'SELECT id' this almost always means the row value was NULL (sql.Scan into string fails) or the driver returned an unexpected column type.","triggerScenarios":"A row in issues has a NULL id column (corrupted data or an aliased/modified query), or the driver returns an id in a type database/sql cannot convert into a Go string.","commonSituations":"A corrupted or manually edited Dolt/MySQL database where issues.id is NULL; running against a schema variant where id is not the first/only returned column; driver version mismatches altering scan behavior.","solutions":["Inspect the issues table for rows with NULL ids (SELECT COUNT(*) FROM issues WHERE id IS NULL) and repair or delete them.","If the schema was customized, restore the standard schema so id is a non-null primary key.","Check the driver version (Dolt/MySQL) matches what the library expects; upgrade or pin it.","If the error persists, capture the wrapped error text to confirm whether it is a NULL-conversion error (sql: Scan error ... converting NULL to string)."],"exampleFix":"// before\n// NULL ids present in corrupted table\n// after\nDELETE FROM issues WHERE id IS NULL; -- repair corrupted rows","handlingStrategy":"validation","validationCode":"// detect NULL ids in the issues table before scanning\nvar nulls int\n_ = tx.QueryRowContext(ctx, `SELECT COUNT(*) FROM issues WHERE id IS NULL`).Scan(&nulls)\nif nulls > 0 { return fmt.Errorf(\"%d issues rows have NULL ids; repair database\", nulls) }","typeGuard":null,"tryCatchPattern":"ids, err := issueops.GetStaleIssuesInTx(ctx, tx, filter)\nif err != nil && strings.Contains(err.Error(), \"converting NULL to string\") {\n    return fmt.Errorf(\"corrupt issues table (NULL id); run repair: %w\", err)\n}","preventionTips":["Never hand-edit the issues table; use bd commands","Run integrity checks after crashes or forced kills","Keep the driver version aligned with the library's expectations","Back up the database before schema experiments"],"tags":["go","sql","scan","data-corruption"],"backgroundTag":"sql-scan-null-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}