diff --git a/framework/py/flwr/common/args.py b/framework/py/flwr/common/args.py index 3bf799f6a6f8..0f7f9b80fb1e 100644 --- a/framework/py/flwr/common/args.py +++ b/framework/py/flwr/common/args.py @@ -89,7 +89,7 @@ def try_obtain_root_certificates( else: # Load the certificates if provided, or load the system certificates if root_cert_path is None: - log(INFO, "Using system certificates") + log(INFO, "Using system certificates for TLS connection") root_certificates = None elif not isfile(root_cert_path): log(ERROR, "Path argument `--root-certificates` does not point to a file.") diff --git a/framework/py/flwr/common/retry_invoker.py b/framework/py/flwr/common/retry_invoker.py index ce603d556f84..26065f6a022f 100644 --- a/framework/py/flwr/common/retry_invoker.py +++ b/framework/py/flwr/common/retry_invoker.py @@ -21,7 +21,7 @@ import time from collections.abc import Callable, Generator, Iterable from dataclasses import dataclass -from logging import INFO, WARN +from logging import ERROR, INFO, WARN from typing import Any, cast import grpc @@ -351,7 +351,12 @@ def _should_giveup_fn(e: Exception) -> bool: if e.code() == grpc.StatusCode.PERMISSION_DENIED: # type: ignore raise RunNotRunningException if e.code() == grpc.StatusCode.UNAVAILABLE: # type: ignore - return False + # Check if this is an SSL handshake failure - these should fail fast + details = str(e.details() if hasattr(e, "details") else "").lower() + if "handshake failed" in details: + log(ERROR, "SSL/TLS handshake error detected.") + return True # Give up on SSL/TLS handshake errors + return False # Retry on other UNAVAILABLE errors (network issues) return True def _wait(wait_time: float) -> None: