Repository: prometheus/prometheus
File: storage/remote/write_handler.go (Go)

Code before:
	contentType = strings.TrimSpace(contentType)

	parts := strings.Split(contentType, ";")
	if parts[0] != appProtoContentType {
		return "", fmt.Errorf("expected %v as the first (media) part, got %v content-type", appProtoContentType, contentType)
	}
	// Parse potential https://www.rfc-editor.org/rfc/rfc9110#parameter
	for _, p := range parts[1:] {
		pair := strings.Split(p, "=")
		if len(pair) != 2 {
			return "", fmt.Errorf("as per https://www.rfc-editor.org/rfc/rfc9110#parameter expected parameters to be key-values, got %v in %v content-type", p, contentType)
		}
		if pair[0] == "proto" {
			ret := config.RemoteWriteProtoMsg(pair[1])
			if err := ret.Validate(); err != nil {
				return "", fmt.Errorf("got %v content type; %w", contentType, err)
			}
			return ret, nil
		}
	}
	// No "proto=" parameter, assuming v1.
	return config.RemoteWriteProtoMsgV1, nil
}

// isHistogramValidationError checks if the error is a native histogram validation error
// that should return HTTP 400 instead of 500.
func isHistogramValidationError(err error) bool {
	return errors.Is(err, histogram.ErrHistogramCountMismatch) ||
		errors.Is(err, histogram.ErrHistogramCountNotBigEnough) ||
		errors.Is(err, histogram.ErrHistogramNegativeBucketCount) ||
		errors.Is(err, histogram.ErrHistogramSpanNegativeOffset) ||
		errors.Is(err, histogram.ErrHistogramSpansBucketsMismatch) ||
		errors.Is(err, histogram.ErrHistogramCustomBucketsMismatch) ||
		errors.Is(err, histogram.ErrHistogramCustomBucketsInvalid) ||
		errors.Is(err, histogram.ErrHistogramCustomBucketsInfinite) ||
		strings.Contains(err.Error(), "custom buckets: must have zero count of 0") ||
		strings.Contains(err.Error(), "custom buckets: must have zero threshold of 0") ||
		strings.Contains(err.Error(), "custom buckets: must not have negative spans") ||
		strings.Contains(err.Error(), "custom buckets: must not have negative buckets") ||
		strings.Contains(err.Error(), "histogram with exponential schema must not have custom bounds")
}

func (h *writeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	contentType := r.Header.Get("Content-Type")
	if contentType == "" {
		// Don't break yolo 1.0 clients if not needed. This is similar to what we did
		// before 2.0: https://github.com/prometheus/prometheus/blob/d78253319daa62c8f28ed47e40bafcad2dd8b586/storage/remote/write_handler.go#L62
		// We could give http.StatusUnsupportedMediaType, but let's assume 1.0 message by default.
		contentType = appProtoContentType
	}


Review comment (bug):
This comment doesn't seem to help or add context to this function. The 500 or 400 error relates to the bug you're fixing, not the function's purpose or objective.

```suggestion
// isHistogramValidationError checks if the error is a native histogram validation error.
```

Code after:
	contentType = strings.TrimSpace(contentType)

	parts := strings.Split(contentType, ";")
	if parts[0] != appProtoContentType {
		return "", fmt.Errorf("expected %v as the first (media) part, got %v content-type", appProtoContentType, contentType)
	}
	// Parse potential https://www.rfc-editor.org/rfc/rfc9110#parameter
	for _, p := range parts[1:] {
		pair := strings.Split(p, "=")
		if len(pair) != 2 {
			return "", fmt.Errorf("as per https://www.rfc-editor.org/rfc/rfc9110#parameter expected parameters to be key-values, got %v in %v content-type", p, contentType)
		}
		if pair[0] == "proto" {
			ret := config.RemoteWriteProtoMsg(pair[1])
			if err := ret.Validate(); err != nil {
				return "", fmt.Errorf("got %v content type; %w", contentType, err)
			}
			return ret, nil
		}
	}
	// No "proto=" parameter, assuming v1.
	return config.RemoteWriteProtoMsgV1, nil
}

// isHistogramValidationError checks if the error is a native histogram validation error.
func isHistogramValidationError(err error) bool {
	// TODO: Consider adding single histogram error type instead of individual sentinel errors.
	return errors.Is(err, histogram.ErrHistogramCountMismatch) ||
		errors.Is(err, histogram.ErrHistogramCountNotBigEnough) ||
		errors.Is(err, histogram.ErrHistogramNegativeBucketCount) ||
		errors.Is(err, histogram.ErrHistogramSpanNegativeOffset) ||
		errors.Is(err, histogram.ErrHistogramSpansBucketsMismatch) ||
		errors.Is(err, histogram.ErrHistogramCustomBucketsMismatch) ||
		errors.Is(err, histogram.ErrHistogramCustomBucketsInvalid) ||
		errors.Is(err, histogram.ErrHistogramCustomBucketsInfinite) ||
		errors.Is(err, histogram.ErrHistogramCustomBucketsZeroCount) ||
		errors.Is(err, histogram.ErrHistogramCustomBucketsZeroThresh) ||
		errors.Is(err, histogram.ErrHistogramCustomBucketsNegSpans) ||
		errors.Is(err, histogram.ErrHistogramCustomBucketsNegBuckets) ||
		errors.Is(err, histogram.ErrHistogramExpSchemaCustomBounds)
}

func (h *writeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	contentType := r.Header.Get("Content-Type")
	if contentType == "" {
		// Don't break yolo 1.0 clients if not needed. This is similar to what we did
		// before 2.0: https://github.com/prometheus/prometheus/blob/d78253319daa62c8f28ed47e40bafcad2dd8b586/storage/remote/write_handler.go#L62
		// We could give http.StatusUnsupportedMediaType, but let's assume 1.0 message by default.
		contentType = appProtoContentType
	}