gofr-dev/gofr · error

%w %q: %w

Error message

%w %q: %w

What it means

NewReader failed to RETRieve the file from the FTP server, but the underlying error was classified as 'object not found' (errObjectNotFound) via isFTPNotFoundError. The library wraps the sentinel with the requested name and the raw FTP error so callers can errors.Is-test for a missing object. It means the path resolved by buildPath does not exist (or is unreachable) on the FTP server.

Source

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

	return nil
}

// 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)
	}

View on GitHub (pinned to 187eb24962)

Solutions

  1. Verify the exact object name and that the file exists: run an FTP LIST or call StatObject on the same name first
  2. Check the base-path/prefix configuration used by buildPath — the effective path is baseDir + name
  3. Remember FTP paths are case-sensitive on most servers; match the case exactly
  4. Handle the not-found case with errors.Is(err, errObjectNotFound) instead of treating it as a transient failure

Example fix

// before
r, err := store.NewReader(ctx, "Data/report.csv")
// after
r, err := store.NewReader(ctx, "data/report.csv")
if err != nil && errors.Is(err, errObjectNotFound) { /* create default file or return 404 */ }
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := store.StatObject(ctx, name); err != nil {
    // object missing; handle before calling NewReader
}

Type guard

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

Try / catch

r, err := store.NewReader(ctx, name)
if err != nil {
    if errors.Is(err, errObjectNotFound) {
        return nil, ErrNotFound // map to 404
    }
    return nil, err
}

Prevention

When it happens

Trigger: Calling storageAdapter.NewReader(ctx, name) where s.conn.Retr(objectPath) returns an error matching FTP status 550/"no such file" patterns detected by isFTPNotFoundError.

Common situations: Typo in file name, wrong base path/prefix configured in buildPath, file deleted by another process, working directory differing from the expected FTP root, case-sensitivity mismatch on Unix FTP servers.

Related errors


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