{"record":{"id":"1e0cc05904b72149","repo":"gofr-dev/gofr","slug":"failed-to-dial-ftp-server-q-w","errorCode":null,"errorMessage":"failed to dial FTP server %q: %w","messagePattern":"failed to dial FTP server %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/gofr/datasource/file/ftp/storage_adapter.go","lineNumber":77,"sourceCode":"\tif s.cfg == nil {\n\t\treturn errFTPConfigNil\n\t}\n\n\tif s.cfg.Host == \"\" || s.cfg.Port <= 0 {\n\t\treturn errFTPConfigInvalid\n\t}\n\n\t// Set default timeout if not specified\n\tdialTimeout := s.cfg.DialTimeout\n\tif dialTimeout == 0 {\n\t\tdialTimeout = 5 * time.Second\n\t}\n\n\tftpServer := fmt.Sprintf(\"%s:%d\", s.cfg.Host, s.cfg.Port)\n\n\tconn, err := ftp.Dial(ftpServer, ftp.DialWithTimeout(dialTimeout))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to dial FTP server %q: %w\", ftpServer, err)\n\t}\n\n\tif err := conn.Login(s.cfg.User, s.cfg.Password); err != nil {\n\t\t_ = conn.Quit()\n\t\treturn fmt.Errorf(\"FTP login failed for user %q: %w\", s.cfg.User, err)\n\t}\n\n\ts.conn = conn\n\n\treturn nil\n}\n\n// NewReader creates a reader for the given object.\nfunc (s *storageAdapter) NewReader(_ context.Context, name string) (io.ReadCloser, error) {\n\tif name == \"\" {\n\t\treturn nil, errEmptyObjectName\n\t}\n","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/gofr-dev/gofr/blob/187eb24962502e91f1fee856230670958b66e89c/pkg/gofr/datasource/file/ftp/storage_adapter.go#L59-L95","documentation":"This error wraps the goftp dial failure when Connect cannot establish a TCP connection to the FTP server at host:port. It uses %w so the root cause (DNS failure, connection refused, timeout) is preserved for errors.Is/As inspection. No login has been attempted at this point.","triggerScenarios":"Calling Connect when the server hostname does not resolve, the port is wrong, the server is down, or the dial exceeds the configured dialTimeout.","commonSituations":"Wrong host/port in config; FTP service not running or blocked by security groups/firewall; DNS misconfiguration in containers/K8s; network egress restrictions from the runtime environment.","solutions":["Verify the server is reachable: nc -vz host port (or telnet) from the same network","Correct Host and Port in the Config","Check firewall/security-group egress rules allow outbound TCP to the FTP port","Retry with backoff for transient network outages; inspect the wrapped cause via errors.Unwrap"],"exampleFix":"// before\ncfg := &ftp.Config{Host: \"ftp.internal\", Port: 2121} // wrong port\n// after\ncfg := &ftp.Config{Host: \"ftp.internal\", Port: 21}\nif err := fs.Connect(cfg); err != nil {\n    return fmt.Errorf(\"connect: %w\", err) // unwrap to see dial cause\n}","handlingStrategy":"retry","validationCode":"// pre-flight reachability check before Connect\nhost, port := cfg.Host, cfg.Port\nconn, err := net.DialTimeout(\"tcp\", fmt.Sprintf(\"%s:%d\", host, port), 5*time.Second)\nif err != nil { return fmt.Errorf(\"ftp server unreachable: %w\", err) }\n_ = conn.Close()","typeGuard":"func isDialError(err error) bool {\n    return strings.HasPrefix(err.Error(), \"failed to dial FTP server\") || errors.Is(err, net.ErrUnknownNetwork) || errors.Is(err, os.ErrDeadlineExceeded)\n}","tryCatchPattern":"err := fs.Connect(cfg)\nvar netErr net.Error\nif errors.As(err, &netErr) || strings.Contains(err.Error(), \"failed to dial\") {\n    // retry with exponential backoff; check host/port/firewall if persistent\n}","preventionTips":["Verify DNS resolution and port with a TCP probe before depending on the service","Open firewall/security-group egress to the FTP control port","Use retries with backoff for transient outages","Keep dial timeouts modest so failures surface quickly with a clear cause"],"tags":["ftp","network","connection","timeout"],"backgroundTag":"connection-refused","analyzedSha":"187eb24962502e91f1fee856230670958b66e89c","analyzedAt":"2026-09-01T20:34:54.554Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}