@@ -529,22 +529,30 @@ async fn send_get_header_impl(
529529 let code = res. status ( ) ;
530530 RELAY_STATUS_CODE . with_label_values ( & [ code. as_str ( ) , GET_HEADER_ENDPOINT_TAG , & relay. id ] ) . inc ( ) ;
531531
532- // Parse Content-Type (tolerating MIME parameters per RFC 7231 §3.1.1.1)
533- // and Eth-Consensus-Version headers in one shot.
534- let ( content_type, fork) = parse_response_encoding_and_fork ( res. headers ( ) , code. as_u16 ( ) ) ?;
535-
536- let response_bytes = safe_read_http_response ( res, MAX_SIZE_GET_HEADER_RESPONSE ) . await ?;
532+ // 204 No Content = relay has no header for this slot/parent_hash/pubkey.
533+ // Return early — there is no body to decode.
537534 if code == StatusCode :: NO_CONTENT {
538535 debug ! (
539536 relay_id = relay. id. as_ref( ) ,
540537 ?code,
541538 latency = ?request_latency,
542- response = ?response_bytes,
543539 "no header from relay"
544540 ) ;
545541 return Ok ( ( start_request_time, None ) ) ;
546542 }
547543
544+ // Parse Content-Type before consuming the response body. Only successful
545+ // responses carry a meaningful Content-Type (SSZ or JSON). Non-success
546+ // responses often carry text/plain (or another default), which would fail
547+ // as unsupported — but safe_read_http_response will return NonSuccess for
548+ // those, so the parsed values are never consumed by the caller.
549+ let ( content_type, fork) = if code. is_success ( ) {
550+ parse_response_encoding_and_fork ( res. headers ( ) , code. as_u16 ( ) ) ?
551+ } else {
552+ ( EncodingType :: Json , None )
553+ } ;
554+ let response_bytes = safe_read_http_response ( res, MAX_SIZE_GET_HEADER_RESPONSE ) . await ?;
555+
548556 Ok ( (
549557 start_request_time,
550558 Some ( GetHeaderResponseInfo {
0 commit comments