{"record":{"id":"b60a4d15a1dcf639","repo":"gofr-dev/gofr","slug":"ftp-login-failed-for-user-q-w","errorCode":null,"errorMessage":"FTP login failed for user %q: %w","messagePattern":"FTP login failed for user %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/gofr/datasource/file/ftp/storage_adapter.go","lineNumber":82,"sourceCode":"\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\n\tif s.conn == nil {\n\t\treturn nil, errFTPClientNotInitialized\n\t}\n\n\tobjectPath := s.buildPath(name)","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/gofr-dev/gofr/blob/187eb24962502e91f1fee856230670958b66e89c/pkg/gofr/datasource/file/ftp/storage_adapter.go#L64-L100","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify User and Password in the Config against the server's account store","Test the same credentials with a manual FTP client (ftp/lftp) to isolate app vs credential issues","Check whether the account is locked, expired, or IP-restricted on the server","Ensure secrets are loaded from the right environment (dev vs prod) and not truncated"],"exampleFix":"// before\ncfg := &ftp.Config{Host: h, Port: 21, User: \"deploy\", Password: oldPass}\n// after\ncfg := &ftp.Config{Host: h, Port: 21, User: \"deploy\", Password: os.Getenv(\"FTP_PASSWORD\")}","handlingStrategy":"validation","validationCode":"// fail fast on missing credentials before Connect\nif cfg.User == \"\" || cfg.Password == \"\" {\n    return errors.New(\"ftp credentials missing: set FTP_USER and FTP_PASSWORD\")\n}","typeGuard":"func isLoginError(err error) bool { return strings.Contains(err.Error(), \"FTP login failed\") }","tryCatchPattern":"err := fs.Connect(cfg)\nif strings.Contains(err.Error(), \"FTP login failed\") {\n    // do not blindly retry — check credentials/account status first\n    return fmt.Errorf(\"check FTP credentials: %w\", err)\n}","preventionTips":["Load credentials from a secret manager; never hardcode or commit passwords","Rotate secrets in lockstep with the FTP server account store","Test credentials manually with lftp/ftp when provisioning a new environment","Limit login retries to avoid account lockout; alert on repeated auth failures"],"tags":["ftp","authentication","credentials"],"backgroundTag":"authentication-failed","analyzedSha":"187eb24962502e91f1fee856230670958b66e89c","analyzedAt":"2026-09-01T20:34:54.554Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}