gofr-dev/gofr · error
%w for %q at offset %d: %w
Error message
%w for %q at offset %d: %w
What it means
NewRangeReader failed at the FTP level for a non-not-found reason: RetrFrom returned an error that isFTPNotFoundError did not match, so the library wraps errFailedToCreateReader along with the name, offset, and underlying error. Indicates connection/server problems or unsupported REST (restart) on the server.
Source
Thrown at pkg/gofr/datasource/file/ftp/storage_adapter.go:136
}
if offset < 0 {
return nil, fmt.Errorf("%w (got: %d)", errInvalidOffset, offset)
}
if s.conn == nil {
return nil, errFTPClientNotInitialized
}
objectPath := s.buildPath(name)
reader, err := s.conn.RetrFrom(objectPath, uint64(offset))
if err != nil {
if isFTPNotFoundError(err) {
return nil, fmt.Errorf("%w %q: %w", errObjectNotFound, name, err)
}
return nil, fmt.Errorf("%w for %q at offset %d: %w", errFailedToCreateReader, name, offset, err)
}
// FTP doesn't support length limit in RetrFrom, so wrap in LimitReader
if length > 0 {
return &limitedReadCloser{
Reader: io.LimitReader(reader, length),
Closer: reader,
}, nil
}
return reader, nil
}
// limitedReadCloser combines io.LimitReader with Close capability.
type limitedReadCloser struct {
io.Reader
io.Closer
}View on GitHub (pinned to 187eb24962)
Solutions
- Verify the server supports REST (byte-range resume); otherwise read the whole object with NewReader and seek client-side
- Check that offset < file size via StatObject before ranging
- Re-dial/reconnect the FTP client and retry if the connection was idle-timed out
- Inspect the wrapped error for the exact FTP response code
- Check permissions for the authenticated FTP user
Example fix
// before
r, err := store.NewRangeReader(ctx, "big.bin", 1<<40, 100) // offset beyond EOF
// after
info, _ := store.StatObject(ctx, "big.bin")
if 1<<40 >= info.Size { return io.EOF }
r, err := store.NewRangeReader(ctx, "big.bin", 1<<40, 100) Defensive patterns
Strategy: retry
Validate before calling
info, err := store.StatObject(ctx, name)
if err != nil { return err }
if offset >= info.Size { return io.EOF } Type guard
func isFTPNotFound(err error) bool { return errors.Is(err, errObjectNotFound) } Try / catch
r, err := store.NewRangeReader(ctx, name, offset, length)
if err != nil && !errors.Is(err, errObjectNotFound) {
// reconnect and retry with backoff; check server REST support on repeated failure
} Prevention
- Confirm the FTP server supports REST/Resume for ranged reads
- Bound offsets by the object size from StatObject
- Re-dial idle FTP connections before issuing ranged commands
When it happens
Trigger: storageAdapter.NewRangeReader where s.conn.RetrFrom(objectPath, uint64(offset)) fails with errors like connection closed, 4xx/5xx server responses, or servers not supporting REST byte ranges.
Common situations: Stale/idle FTP connection dropped before the command, FTP server without REST support, offset beyond EOF causing a server-side error, permission issues.
Related errors
- failed to create reader
- failed to list objects
- failed to dial FTP server %q: %w
- %w for %q: %w
- %w (got: %d)
AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01).
Data as JSON: /api/errors/fd4d7d98aa475fea.
Report an issue: GitHub.