go-sql-driver/mysql · error
reader '%s' is <nil>
Error message
reader '%s' is <nil>
What it means
Thrown by handleInFileRequest (infile.go:115) during a 'LOAD DATA LOCAL INFILE Reader::<name>' operation when the registered handler function for <name> exists but returned nil instead of an io.Reader. The driver cannot read from a nil reader, so the LOAD DATA fails. The %s is the reader name from the SQL.
Source
Thrown at infile.go:115
var rdr io.Reader
packetSize := min(mc.maxWriteSize, defaultPacketSize)
if idx := strings.Index(name, "Reader::"); idx == 0 || (idx > 0 && name[idx-1] == '/') { // io.Reader
// The server might return an an absolute path. See issue #355.
name = name[idx+8:]
readerRegisterLock.RLock()
handler, inMap := readerRegister[name]
readerRegisterLock.RUnlock()
if inMap {
rdr = handler()
if rdr != nil {
if cl, ok := rdr.(io.Closer); ok {
defer deferredClose(&err, cl)
}
} else {
err = fmt.Errorf("reader '%s' is <nil>", name)
}
} else {
err = fmt.Errorf("reader '%s' is not registered", name)
}
} else { // File
name = strings.Trim(name, `"`)
fileRegisterLock.RLock()
_, exists := fileRegister[name]
fileRegisterLock.RUnlock()
if mc.cfg.AllowAllFiles || exists {
var file *os.File
var fi os.FileInfo
if file, err = os.Open(name); err == nil {
defer deferredClose(&err, file)
// get file size
if fi, err = file.Stat(); err == nil {View on GitHub (pinned to c426bd9379)
Solutions
- Make the registered handler never return nil — return a real io.Reader (e.g. an empty bytes.Reader if there's no data) or return a reader that surfaces the underlying open error.
- If the source genuinely has no data, return io.MultiReader() or bytes.NewReader(nil) so the LOAD completes with zero rows.
- Move the open() out of the handler so failures surface before LOAD DATA is issued, and the handler only wraps an already-open reader.
- Log inside the handler when it would have returned nil, to catch regressions.
Example fix
// before
mysql.RegisterReaderHandler("data", func() io.Reader {
f, err := os.Open("/tmp/data.csv")
if err != nil {
return nil // triggers 'reader data is <nil>'
}
return f
})
// after: never return nil; surface the error or return an empty reader
mysql.RegisterReaderHandler("data", func() io.Reader {
f, err := os.Open("/tmp/data.csv")
if err != nil {
return bytes.NewReader(nil) // zero rows instead of a crash
}
return f
}) Defensive patterns
Strategy: validation
Validate before calling
// wrap any handler so it can never return nil
func safeReaderHandler(h func() (io.Reader, error)) func() io.Reader {
return func() io.Reader {
r, err := h()
if err != nil || r == nil {
return bytes.NewReader(nil) // zero-row load instead of '<nil>' error
}
return r
}
}
mysql.RegisterReaderHandler("data", safeReaderHandler(func() (io.Reader, error) {
return os.Open("/tmp/data.csv")
})) Type guard
// isValidReaderHandler checks a factory never yields nil
func isValidReaderHandler(h func() io.Reader) bool {
// best-effort: only call if the handler is side-effect free; otherwise
// ensure at registration that the contract forbids nil returns
return h != nil
} Try / catch
if _, err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE t"); err != nil {
if strings.Contains(err.Error(), "is <nil>") {
// fix the handler to return an empty reader and retry
}
} Prevention
- Never let a registered handler return nil; return an empty reader for the no-data case.
- Open resources before registering the handler so factory failures surface earlier.
- Add a unit test asserting each handler returns a non-nil reader.
When it happens
Trigger: Registering a reader via mysql.RegisterReaderHandler("data", func() io.Reader { ... }) whose factory returns nil (e.g. the underlying file/resource could not be opened), then executing 'LOAD DATA LOCAL INFILE Reader::data INTO TABLE ...'.
Common situations: The handler tries to open a file/stream at call time and returns nil on error instead of a sentinel reader; lazy initialization that hasn't completed; a handler that returns nil for 'no data today' as a convention; refactoring that broke the handler's contract.
Related errors
- reader '%s' is not registered
- mysql: driver does not support the use of Named Parameters
- mysql: unsupported isolation level: %v
- local file '%s' is not registered
- argument count mismatch (got: %d; has: %d)
AI-assisted analysis of go-sql-driver/mysql@c426bd9379 (2026-08-04).
Data as JSON: /data/errors/bc251e1367970b7d.json.
Report an issue: GitHub.