go-sql-driver/mysql · error

reader '%s' is not registered

Error message

reader '%s' is not registered

What it means

Thrown by handleInFileRequest (infile.go:118) during a 'LOAD DATA LOCAL INFILE Reader::<name>' operation when no handler has been registered under <name> in the readerRegister map. The driver refuses to honor an unregistered reader name (a safety measure, mirroring the file allowlist) rather than failing later. The %s is the missing name.

Source

Thrown at infile.go:118

	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 {
					rdr = file
					if fileSize := int(fi.Size()); fileSize < packetSize {
						packetSize = fileSize

View on GitHub (pinned to c426bd9379)

Solutions

  1. Call mysql.RegisterReaderHandler with the exact same name string used in the SQL before issuing the LOAD DATA.
  2. Check for typos, trailing whitespace, and case differences between the SQL 'Reader::<name>' and the registration key.
  3. Ensure registration runs in the same binary/process that executes the LOAD (the registry is in-memory, not shared across processes).
  4. If using a framework that lazy-initializes handlers, register eagerly at startup to avoid ordering bugs.

Example fix

// before: name in SQL not registered
// db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE t")

// after: register the exact name first
mysql.RegisterReaderHandler("data", func() io.Reader {
    return strings.NewReader("a,b\n1,2\n")
})
db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE t")
Defensive patterns

Strategy: validation

Validate before calling

// ensure a reader is registered before issuing LOAD DATA LOCAL INFILE Reader::<name>
func ensureReader(name string, h func() io.Reader) {
    mysql.RegisterReaderHandler(name, h) // idempotent registration
}
func loadFromReader(db *sql.DB, name string) error {
    // RegisterReaderHandler overwrites, so registering here is safe
    mysql.RegisterReaderHandler(name, func() io.Reader { return strings.NewReader("") })
    _, err := db.Exec(fmt.Sprintf("LOAD DATA LOCAL INFILE 'Reader::%s' INTO TABLE t", name))
    return err
}

Try / catch

if _, err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE t"); err != nil {
    if strings.Contains(err.Error(), "is not registered") {
        // register the handler then retry
    }
}

Prevention

When it happens

Trigger: Running 'LOAD DATA LOCAL INFILE Reader::mydata INTO TABLE ...' without first calling mysql.RegisterReaderHandler("mydata", ...), or with a typo in the name, or after DeregisterReaderHandler removed it.

Common situations: Forgot to register the handler in the new code path; name mismatch between the SQL string and the registration (case sensitivity, trailing spaces); handler was registered in a different process/binary; DeregisterReaderHandler ran before the LOAD; refactor that dropped the registration call.

Related errors


AI-assisted analysis of go-sql-driver/mysql@c426bd9379 (2026-08-04). Data as JSON: /data/errors/d71a62099c95b45d.json. Report an issue: GitHub.