gofr-dev/gofr · error

FTP login failed for user %q: %w

Error message

FTP login failed for user %q: %w

What it means

This error wraps the failure of the FTP LOGIN command after a successful dial. The server was reachable but rejected the credentials for the configured user. The adapter closes the connection (Quit) before returning, so the returned error is the only diagnostic.

Source

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

		return errFTPConfigInvalid
	}

	// Set default timeout if not specified
	dialTimeout := s.cfg.DialTimeout
	if dialTimeout == 0 {
		dialTimeout = 5 * time.Second
	}

	ftpServer := fmt.Sprintf("%s:%d", s.cfg.Host, s.cfg.Port)

	conn, err := ftp.Dial(ftpServer, ftp.DialWithTimeout(dialTimeout))
	if err != nil {
		return fmt.Errorf("failed to dial FTP server %q: %w", ftpServer, err)
	}

	if err := conn.Login(s.cfg.User, s.cfg.Password); err != nil {
		_ = conn.Quit()
		return fmt.Errorf("FTP login failed for user %q: %w", s.cfg.User, err)
	}

	s.conn = conn

	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)

View on GitHub (pinned to 187eb24962)

Solutions

  1. Verify User and Password in the Config against the server's account store
  2. Test the same credentials with a manual FTP client (ftp/lftp) to isolate app vs credential issues
  3. Check whether the account is locked, expired, or IP-restricted on the server
  4. Ensure secrets are loaded from the right environment (dev vs prod) and not truncated

Example fix

// before
cfg := &ftp.Config{Host: h, Port: 21, User: "deploy", Password: oldPass}
// after
cfg := &ftp.Config{Host: h, Port: 21, User: "deploy", Password: os.Getenv("FTP_PASSWORD")}
Defensive patterns

Strategy: validation

Validate before calling

// fail fast on missing credentials before Connect
if cfg.User == "" || cfg.Password == "" {
    return errors.New("ftp credentials missing: set FTP_USER and FTP_PASSWORD")
}

Type guard

func isLoginError(err error) bool { return strings.Contains(err.Error(), "FTP login failed") }

Try / catch

err := fs.Connect(cfg)
if strings.Contains(err.Error(), "FTP login failed") {
    // do not blindly retry — check credentials/account status first
    return fmt.Errorf("check FTP credentials: %w", err)
}

Prevention

When it happens

Trigger: Calling Connect when cfg.User/cfg.Password are wrong, the account is disabled or expired, the server requires TLS/auth not negotiated, or the user is not permitted from the client's IP.

Common situations: Rotated passwords not updated in env/config; anonymous FTP disabled while using user 'anonymous' or empty credentials; account locked after failed attempts; IP allowlist on the FTP server blocking the caller.

Related errors


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