gofr-dev/gofr · error

%w for %q: %w

Error message

%w for %q: %w

What it means

NewReader failed to open the file for a reason other than not-found: RETR returned an error that isFTPNotFoundError did not classify as missing-object, so the library wraps it with errFailedToCreateReader. This represents transient or server-side failures (connection drops, permission denied, server busy).

Source

Thrown at pkg/gofr/datasource/file/ftp/storage_adapter.go:108

// NewReader creates a reader for the given object.
func (s *storageAdapter) NewReader(_ context.Context, name string) (io.ReadCloser, error) {
	if name == "" {
		return nil, errEmptyObjectName
	}

	if s.conn == nil {
		return nil, errFTPClientNotInitialized
	}

	objectPath := s.buildPath(name)

	reader, err := s.conn.Retr(objectPath)
	if err != nil {
		if isFTPNotFoundError(err) {
			return nil, fmt.Errorf("%w %q: %w", errObjectNotFound, name, err)
		}

		return nil, fmt.Errorf("%w for %q: %w", errFailedToCreateReader, name, err)
	}

	return reader, nil
}

// NewRangeReader creates a range reader for the given object.
func (s *storageAdapter) NewRangeReader(_ context.Context, name string, offset, length int64) (io.ReadCloser, error) {
	if name == "" {
		return nil, errEmptyObjectName
	}

	if offset < 0 {
		return nil, fmt.Errorf("%w (got: %d)", errInvalidOffset, offset)
	}

	if s.conn == nil {
		return nil, errFTPClientNotInitialized
	}

View on GitHub (pinned to 187eb24962)

Solutions

  1. Inspect the wrapped cause with errors.Unwrap to see the raw FTP response code
  2. Reconnect/re-establish the FTP connection if it was closed or timed out (retry the operation)
  3. Check file permissions on the FTP server for the authenticated user
  4. If the file truly does not exist but returns a non-standard message, verify isFTPNotFoundError covers your server's response format

Example fix

// before
r, err := store.NewReader(ctx, "data/report.csv") // fails after idle timeout
// after
if err := ftpConn.Close(); err != nil { }
ftpConn, _ = ftp.DialAndLogin(...) // re-dial before retrying
r, err := store.NewReader(ctx, "data/report.csv")
Defensive patterns

Strategy: retry

Validate before calling

if store == nil || ftpConn == nil {
    return errors.New("ftp client not initialized")
}

Type guard

func isFTPNotFound(err error) bool { return errors.Is(err, errObjectNotFound) }

Try / catch

r, err := store.NewReader(ctx, name)
if err != nil && !errors.Is(err, errObjectNotFound) {
    // transient: reconnect FTP and retry with backoff
    r, err = retryWithBackoff(3, func() (io.ReadCloser, error) { return store.NewReader(ctx, name) })
}

Prevention

When it happens

Trigger: storageAdapter.NewReader called when s.conn.Retr(objectPath) fails with any error that is not classified as FTP not-found (e.g. 4xx/5xx responses, closed connection, permission denied).

Common situations: FTP connection dropped/idle-timed out before the call, insufficient permissions on the file, server out of sessions, network interruption mid-command, TLS renegotiation failure.

Related errors


AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01). Data as JSON: /api/errors/3bd8123df446c8e8. Report an issue: GitHub.