Repository: prometheus/prometheus (language: go)

Issue:
Queries failing after upgrade to v2.52.0rc0
### What did you do?

Upgrade to v2.52.0rc0

### What did you expect to see?

Everything continues working just fine

### What did you see instead? Under which circumstances?

Several queries failing (e.g. on Node Exporter Full Dashboard):

Status: 200. Message: ReadArray: expect [ or , or ] or n, but found {, error found in #10 byte of ...|80116"]]}{"metric":{|..., bigger context ...|923262980116"],[1714483080,"47.15923262980116"]]}{"metric":{"device":"/dev/nvme1n1p1","fstype":"vfat|...

Query:

100 - ((node_filesystem_avail_bytes{instance="localhost:9100",job="node_exporter",device!~'rootfs'} * 100) / node_filesystem_size_bytes{instance="localhost:9100",job="node_exporter",device!~'rootfs'})

### System information

Ubuntu 24.04 LTS (GNU/Linux 6.8.0-31-generic x86_64)

### Prometheus version

```text
prometheus, version 2.52.0-rc.0 (branch: HEAD, revision: f170a014f31cc97d2aba47cc769e846c68f8545d)
  build user:       root@969a4b73703c
  build date:       20240430-12:21:35
  go version:       go1.22.2
  platform:         linux/amd64
  tags:             netgo,builtinassets,stringlabels
```


### Prometheus configuration file

_No response_

### Alertmanager version

_No response_

### Alertmanager configuration file

_No response_

### Logs

_No response_


Discussion / hints:
Thanks for trying the release candidate so quickly!

Could you please any additional information that could help us debug the problem, e.g. logs from the Prometheus instance that is failing
Sure, this is what I can see currently in the prometheus log file

```log
ts=2024-04-30T14:09:19.769Z caller=api.go:1769 level=error component=web msg="error writing response" bytesWritten=0 err="write tcp 127.0.0.1:9090->127.0.0.1:43910: write: broken pipe"
ts=2024-04-30T14:09:19.778Z caller=api.go:1769 level=error component=web msg="error writing response" bytesWritten=0 err="write tcp 127.0.0.1:9090->127.0.0.1:43912: write: broken pipe"
ts=2024-04-30T14:09:40.963Z caller=api.go:1769 level=error component=web msg="error writing response" bytesWritten=0 err="write tcp 127.0.0.1:9090->127.0.0.1:37742: write: broken pipe"
ts=2024-04-30T14:09:40.972Z caller=api.go:1769 level=error component=web msg="error writing response" bytesWritten=0 err="write tcp 127.0.0.1:9090->127.0.0.1:37752: write: broken pipe"
ts=2024-04-30T14:09:49.962Z caller=api.go:1769 level=error component=web msg="error writing response" bytesWritten=0 err="write tcp 127.0.0.1:9090->127.0.0.1:43922: write: connection reset by peer"
ts=2024-04-30T14:09:49.980Z caller=api.go:1769 level=error component=web msg="error writing response" bytesWritten=0 err="write tcp 127.0.0.1:9090->127.0.0.1:37768: write: connection reset by peer"
```

(querying prometheus via grafana)
Is it the same if you send the same query through the Prometheus UI/API? If not, could you paste the error from the HTTP response?
Looks like same bug as this:

https://github.com/prometheus/prometheus/issues/14040

Prometheus is returning malformed JSON

Fix patch:
diff --git a/web/api/v1/json_codec.go b/web/api/v1/json_codec.go
index dfcdf78f8a9..f07e57696dd 100644
--- a/web/api/v1/json_codec.go
+++ b/web/api/v1/json_codec.go
@@ -241,8 +241,11 @@ func labelsIsEmpty(ptr unsafe.Pointer) bool {
 func unsafeMarshalVectorJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
 	v := *((*promql.Vector)(ptr))
 	stream.WriteArrayStart()
-	for _, s := range v {
+	for i, s := range v {
 		marshalSampleJSON(s, stream)
+		if i != len(v)-1 {
+			stream.WriteMore()
+		}
 	}
 	stream.WriteArrayEnd()
 }
@@ -251,8 +254,11 @@ func unsafeMarshalVectorJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
 func unsafeMarshalMatrixJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
 	m := *((*promql.Matrix)(ptr))
 	stream.WriteArrayStart()
-	for _, s := range m {
+	for i, s := range m {
 		marshalSeriesJSON(s, stream)
+		if i != len(m)-1 {
+			stream.WriteMore()
+		}
 	}
 	stream.WriteArrayEnd()
 }


Test patch:
diff --git a/web/api/v1/json_codec_test.go b/web/api/v1/json_codec_test.go
index b8384baaa8d..759dabd28e8 100644
--- a/web/api/v1/json_codec_test.go
+++ b/web/api/v1/json_codec_test.go
@@ -29,6 +29,40 @@ func TestJsonCodec_Encode(t *testing.T) {
 		response interface{}
 		expected string
 	}{
+		{
+			response: &QueryData{
+				ResultType: parser.ValueTypeVector,
+				Result: promql.Vector{
+					promql.Sample{
+						Metric: labels.FromStrings("__name__", "foo"),
+						T:      1000,
+						F:      1,
+					},
+					promql.Sample{
+						Metric: labels.FromStrings("__name__", "bar"),
+						T:      2000,
+						F:      2,
+					},
+				},
+			},
+			expected: `{"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"foo"},"value":[1,"1"]},{"metric":{"__name__":"bar"},"value":[2,"2"]}]}}`,
+		},
+		{
+			response: &QueryData{
+				ResultType: parser.ValueTypeMatrix,
+				Result: promql.Matrix{
+					promql.Series{
+						Metric: labels.FromStrings("__name__", "foo"),
+						Floats: []promql.FPoint{{F: 1, T: 1000}},
+					},
+					promql.Series{
+						Metric: labels.FromStrings("__name__", "bar"),
+						Floats: []promql.FPoint{{F: 2, T: 2000}},
+					},
+				},
+			},
+			expected: `{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"__name__":"foo"},"values":[[1,"1"]]},{"metric":{"__name__":"bar"},"values":[[2,"2"]]}]}}`,
+		},
 		{
 			response: &QueryData{
 				ResultType: parser.ValueTypeMatrix,