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
- Inspect the wrapped cause with errors.Unwrap for the exact FTP response code
- Re-dial/reconnect the FTP client and retry the copy if the connection was stale
- Check read permissions of the FTP user on the source path
- 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
- Re-dial idle FTP connections before long copy operations
- Check the FTP user's read permissions on source paths
- Retry transient 4xx server responses with backoff
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
- source and destination names cannot be empty
- source and destination are the same
- failed to create reader
- failed to list objects
- failed to dial FTP server %q: %w
AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01).
Data as JSON: /api/errors/a52c295567626ca3.
Report an issue: GitHub.