gofr-dev/gofr · error

failed to read source object %q: %w

Error message

failed to read source object %q: %w

What it means

CopyObject could not read the source object for a non-not-found reason: Retr failed with an error isFTPNotFoundError did not classify as missing, so it's wrapped in a 'failed to read source object' error with the source name and cause. This covers connection, permission, and server-side failures during the source read phase.

Source

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

	if source == dest {
		return errSameSourceAndDest
	}

	if s.conn == nil {
		return errFTPClientNotInitialized
	}

	// Read source file
	sourcePath := s.buildPath(source)

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

		return fmt.Errorf("failed to read source object %q: %w", source, err)
	}

	// Read all data from source into memory
	data, err := io.ReadAll(resp)

	closeErr := resp.Close()

	if err != nil {
		return fmt.Errorf("failed to read source object data %q: %w", source, err)
	}

	if closeErr != nil {
		return fmt.Errorf("failed to close source reader for %q: %w", source, closeErr)
	}

	// Write to destination
	destPath := s.buildPath(dest)
	if err := s.conn.Stor(destPath, bytes.NewReader(data)); err != nil {

View on GitHub (pinned to 187eb24962)

Solutions

  1. Inspect the wrapped cause with errors.Unwrap for the exact FTP response code
  2. Re-dial/reconnect the FTP client and retry the copy if the connection was stale
  3. Check read permissions of the FTP user on the source path
  4. Retry with backoff for transient 4xx server responses

Example fix

// before
err := store.CopyObject(ctx, "src/a.bin", "dst/a.bin") // stale connection
// after
ftpConn, _ = reconnectFTP(cfg) // fresh connection
err = store.CopyObject(ctx, "src/a.bin", "dst/a.bin")
Defensive patterns

Strategy: retry

Validate before calling

if ftpConn == nil || !ftpConnected(ftpConn) {
    return errors.New("ftp connection unavailable before copy")
}

Type guard

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

Try / catch

err := store.CopyObject(ctx, source, dst)
if err != nil && !errors.Is(err, errObjectNotFound) {
    // reconnect FTP and retry with backoff; check permissions if persistent
}

Prevention

When it happens

Trigger: storageAdapter.CopyObject where s.conn.Retr(sourcePath) fails with connection errors, permission-denied, or other non-550 responses; also if io.ReadAll/resp.Close fails later (those produce their own messages, but this one fires at Retr time).

Common situations: Idle FTP connection timed out before the copy, source file permissions insufficient, transient server 4xx (busy), TLS session issues, very large files hitting server limits mid-transfer.

Related errors


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