influxdata / influxdata/telegraf

XPath deserialization from protobuf fails when consuming non-optional default values

Open
#17,042 2 comments 2 reactions 1 assignee View on GitHub

@skartikey is already working on this.

Since Jul 16, 2025.

bug
Dominant language
Go
Stars
17.8k
Forks
5.8k
Avg merge
1d 20h
Merged PRs (30d)
161

Description

### Relevant telegraf.conf

```toml
[agent]
debug = true
omit_hostname = true

[[outputs.influxdb_v2]]
urls = ["http://influxdb:8086"]
token = "my-super-secret-token"
organization = "my-org"
bucket = "telemetry"

[[inputs.kafka_consumer]]
brokers = ["kafka:29092"]
topics = ["telegraf"]
consumer_group = "telegraf_metrics"
data_format = "xpath_protobuf"
xpath_protobuf_type = "telemetry.TelemetryPoint"
xpath_protobuf_file = "/etc/telegraf/telemetry.proto"
name_override = "telemetry"
[[inputs.kafka_consumer.xpath]]
[inputs.kafka_consumer.xpath.fields]
value = "number(value)"
[inputs.kafka_consumer.xpath.tags]
name = "string(name)"
```

### Logs from Telegraf

```text
2025-05-20T00:13:21Z D! [parsers.xpath_protobuf::kafka_consumer] Number of configs: 1
2025-05-20T00:13:21Z D! [parsers.xpath_protobuf::kafka_consumer] Number of selected metric nodes: 1
2025-05-20T00:13:21Z D! [serializers.influx] could not serialize field "value": is NaN; discarding field
2025-05-20T00:13:21Z D! [outputs.influxdb_v2] Wrote batch of 1 metrics in 76.092µs
2025-05-20T00:13:21Z D! [outputs.influxdb_v2] Buffer fullness: 0 / 10000 metrics
```

### System info

Telegraf 1.34.3, RHEL 8, Podman

### Docker

```yaml
version: "3.8"

services:
zookeeper:
image: confluentinc/cp-zookeeper:7.9.1
environment:
ZOOKEEPER_CLIENT_PORT: 2181

kafka:
image: confluentinc/cp-kafka:7.9.1
depends_on: [zookeeper]
ports:
- "9092:9092" # For host
- "29092:29092" # For Docker containers
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,DOCKER://0.0.0.0:29092
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092,DOCKER://kafka:29092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,DOCKER:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_BROKER_ID: 1
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
KAFKA_CREATE_TOPICS: "telegraf"

influxdb:
image: influxdb:2.7.11
ports:
- "8086:8086"
environment:
DOCKER_INFLUXDB_INIT_MODE: setup
DOCKER_INFLUXDB_INIT_USERNAME: telegraf
DOCKER_INFLUXDB_INIT_PASSWORD: supersecret
DOCKER_INFLUXDB_INIT_ORG: my-org
DOCKER_INFLUXDB_INIT_BUCKET: telemetry
DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: my-super-secret-token

telegraf:
image: telegraf:latest
depends_on:
- kafka
- influxdb
restart: on-failure
volumes:
- ./telegraf/telegraf.conf:/etc/telegraf/telegraf.conf:ro
- ./telemetry.proto:/etc/telegraf/telemetry.proto:ro
environment:
- HOSTNAME=telegraf
links:
- kafka
- influxdb
```

### Steps to reproduce

I have a protobuf file such as
```proto
syntax = "proto3";

package telemetry;

message TelemetryPoint {
float value = 1;
string name = 2;
}
```

which I am trying to ingest into InfluxDB using telegraf. I believe I setup the XPath consumer correctly, shown above.

After generating a protobuf python library with

```sh
grpc_tools.protoc -I. --python_out=. telemetry.proto
```

I send a message to kafka with
```python
from kafka import KafkaProducer
import telemetry_pb2
producer = KafkaProducer(bootstrap_servers="localhost:9092")
msg = telemetry_pb2.TelemetryPoint()
msg.value = 0.0
msg.name = "test"
future = producer.send("telegraf", msg.SerializeToString())
result = future.get(timeout=10)
print(result)
```
after starting up the stack with docker compose.

### Expected behavior

When I send non-zero values, things work as expected, getting the value as a normal metric in influx db. When I write 0.0, not missing or anything, I expect to get zeros.

### Actual behavior

Instead, I get the error message

```
2025-05-20T00:13:31Z D! [serializers.influx] could not serialize field "value": is NaN; discarding field
```

with no data added to the database.

### Additional info

This seems to be a correctness bug with however the protobuf bytes are getting deserialized. The protobuf spec doesn't serialize default values, but deserializers know this and should back-fill in the defaults, which doesn't seem to be happening here.

We see this when we enable `xpath_print_document` and see

```
XML document equivalent: "test"
```

which is an incorrect deserialization of the protobuf.

I believe this issue is in https://github.com/influxdata/telegraf/blob/88442e3ab9f6a41d8e9b5409628204fe6e406d07/plugins/parsers/xpath/protocolbuffer_document.go#L107-L118, where after we unmarshal the bytes but before we call `protobufquery.Parse` we need to inject default values. I believe we can use protoreflect to do this by walking the message fields recursively and setting defaults when fields are unset.

Things work as expected, of course, in proto2 if we
```proto
syntax = "proto2";

package telemetry;

message TelemetryPoint {
required float value = 1;
required string name = 2;
}
```

Finally, things seem to work correctly in protobuf "editions", of course, where the default values are back to being serialized by default.
```proto
edition = "2023";

package telemetry;

message TelemetryPoint {
float value = 1;
string name = 2;
}
```

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.