{"record":{"id":"e70ec61a4f72b1fb","repo":"vectordotdev/vector","slug":"target-tps-cannot-be-nan","errorCode":null,"errorMessage":"target_tps cannot be Nan","messagePattern":"target_tps cannot be Nan","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/sources/datadog_agent/traces.rs","lineNumber":150,"sourceCode":"    let enriched_events = trace_events\n        .into_iter()\n        .map(|mut trace_event| {\n            if let Some(k) = &api_key {\n                trace_event\n                    .metadata_mut()\n                    .set_datadog_api_key(Arc::clone(k));\n            }\n            trace_event.insert(\n                &source.log_schema_source_type_key,\n                Bytes::from(\"datadog_agent\"),\n            );\n            trace_event.insert(event_path!(\"payload_version\"), \"v2\".to_string());\n            trace_event.insert(&source.log_schema_host_key, hostname.clone());\n            trace_event.insert(event_path!(\"env\"), env.clone());\n            trace_event.insert(event_path!(\"agent_version\"), agent_version.clone());\n            trace_event.insert(\n                event_path!(\"target_tps\"),\n                Value::Float(NotNan::new(target_tps).expect(\"target_tps cannot be Nan\")),\n            );\n            trace_event.insert(\n                event_path!(\"error_tps\"),\n                Value::Float(NotNan::new(error_tps).expect(\"error_tps cannot be Nan\")),\n            );\n            if let Some(Value::Object(span_tags)) = trace_event.get_mut(event_path!(\"tags\")) {\n                span_tags.extend(tags.clone());\n            } else {\n                trace_event.insert(event_path!(\"tags\"), Value::from(tags.clone()));\n            }\n            Event::Trace(trace_event)\n        })\n        .collect();\n    Ok(enriched_events)\n}\n\nfn convert_dd_tracer_payload(payload: ddtrace_proto::TracerPayload) -> Vec<TraceEvent> {\n    let tags = convert_tags(payload.tags);","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/vectordotdev/vector/blob/3708c39b12a93212ed8b8d7510b4cc7769cb5864/src/sources/datadog_agent/traces.rs#L132-L168","documentation":"The datadog_agent traces endpoint decodes the ddtrace v2 protobuf payload and copies the top-level target_tps double into the trace event as Value::Float(NotNan::new(target_tps).expect(\"target_tps cannot be Nan\")). ordered_float::NotNan::new returns Err exactly when the f64 is NaN. Unlike JSON, a protobuf double can carry the NaN bit pattern, so the assumption 'the agent never sends NaN' is enforced with a panic.","triggerScenarios":"POSTing a TracePayload protobuf to the datadog_agent traces endpoint with the target_tps field set to the NaN bit pattern (0x7FF8...), or a corrupted frame whose bytes decode to NaN. handle_dd_trace_payload_v1 then panics while enriching the trace events.","commonSituations":"Corrupt or hostile agent payloads, custom tracers built against ddtrace_proto, proxies mangling bodies. One NaN field aborts the whole trace batch conversion.","solutions":["Guard before inserting: use if let Some(tps) = NotNan::new(target_tps) and skip the field insert (with a warning) on Err","Validate the decoded payload's float fields right after TracePayload::decode and reject the request with 422","Upgrade Vector once input hardening lands"],"exampleFix":"// before\ntrace_event.insert(\n    event_path!(\"target_tps\"),\n    Value::Float(NotNan::new(target_tps).expect(\"target_tps cannot be Nan\")),\n);\n\n// after\nif let Some(tps) = NotNan::new(target_tps) {\n    trace_event.insert(event_path!(\"target_tps\"), Value::Float(tps));\n} else {\n    warn!(message = \"dropping NaN target_tps from datadog agent payload\");\n}","handlingStrategy":"validation","validationCode":"fn finite_or_zero(v: f64) -> f64 {\n    if v.is_finite() { v } else { 0.0 }\n}\n\n// before insert:\nif !target_tps.is_finite() {\n    warn!(value = target_tps, \"non-finite target_tps from agent payload\");\n    target_tps = 0.0;\n}","typeGuard":"fn as_not_nan(v: f64) -> Option<ordered_float::NotNan<f64>> {\n    ordered_float::NotNan::new(v).ok()\n}","tryCatchPattern":"if let Some(tps) = NotNan::new(target_tps) {\n    trace_event.insert(event_path!(\"target_tps\"), Value::Float(tps));\n} else {\n    warn!(message = \"dropping NaN target_tps\");\n}","preventionTips":["Check is_finite() (covers NaN and infinities) on every float decoded from protobuf before use","Reject payloads with non-finite doubles at the decode boundary with a 4xx response","Fuzz the traces endpoint with NaN/Inf double fields"],"tags":["rust","panic","nan","ordered-float","datadog-agent","traces","protobuf"],"backgroundTag":"nan-in-numeric-field","analyzedSha":"3708c39b12a93212ed8b8d7510b4cc7769cb5864","analyzedAt":"2026-08-20T07:02:18.786Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}