{"record":{"id":"9b6dca7dd5a8e8cf","repo":"microsoft/aspire","slug":"hex-string-must-have-an-even-length","errorCode":null,"errorMessage":"Hex string must have an even length.","messagePattern":"Hex string must have an even length\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/Aspire.Dashboard/Otlp/Model/Serialization/OtlpJsonProtobufConverter.cs","lineNumber":871,"sourceCode":"        return kvList;\n    }\n\n    /// <summary>\n    /// Converts a hexadecimal string to a ByteString.\n    /// </summary>\n    /// <param name=\"hex\">The hexadecimal string to convert.</param>\n    /// <returns>A ByteString containing the decoded bytes.</returns>\n    /// <exception cref=\"ArgumentException\">Thrown when the hex string has an odd length.</exception>\n    internal static ByteString HexToByteString(string hex)\n    {\n        if (string.IsNullOrEmpty(hex))\n        {\n            return ByteString.Empty;\n        }\n\n        if (hex.Length % 2 != 0)\n        {\n            throw new ArgumentException(\"Hex string must have an even length.\", nameof(hex));\n        }\n\n        var hexSpan = hex.AsSpan();\n        var bytes = new byte[hex.Length / 2];\n        for (var i = 0; i < bytes.Length; i++)\n        {\n            bytes[i] = byte.Parse(hexSpan.Slice(i * 2, 2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture);\n        }\n        return ByteString.CopyFrom(bytes);\n    }\n}\n\n/// <summary>\n/// Converts protobuf types to OTLP JSON types.\n/// </summary>\ninternal static class OtlpProtobufToJsonConverter\n{\n    /// <summary>","sourceCodeStart":853,"sourceCodeEnd":889,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Dashboard/Otlp/Model/Serialization/OtlpJsonProtobufConverter.cs#L853-L889","documentation":"HexToByteString converts hex-encoded ids (trace ids, span ids) back into protobuf ByteString values. A hex string with an odd number of characters cannot be split into whole bytes, so the method throws this ArgumentException for nameof(hex). This protects the OTLP protobuf conversion (ToProtobuf) from producing truncated or misaligned byte arrays.","triggerScenarios":"HexToByteString is called from ToProtobuf (OtlpJsonProtobufConverter) when converting JSON-serialized telemetry back to protobuf; it throws whenever the hex string is non-empty but hex.Length % 2 != 0. Caused by malformed/stored JSON where an id field contains a truncated hex value.","commonSituations":"Manually edited or truncated telemetry JSON files; ids produced by a custom serializer that dropped a character; copy/paste of hex ids with a character lost; migrating telemetry stored in an older/incompatible format.","solutions":["Fix the source of the hex string so it always emits an even number of hex characters (zero-pad ids to the fixed width, e.g. 32 chars for trace ids, 16 for span ids).","Validate/repair the stored JSON before conversion: pad the odd-length hex value with a leading zero or discard the record.","Check the custom converter/serializer that produced the JSON; ids should be hex-encoded without dropping leading zeros."],"exampleFix":"// before\nnew Span { SpanId = spanId } // spanId = \"abc\" (odd length)\n// after: pad to fixed width before conversion\nvar padded = spanId.Length % 2 == 0 ? spanId : \"0\" + spanId;\nnew Span { SpanId = padded }","handlingStrategy":"validation","validationCode":"// validate hex ids before conversion\nbool isValidHexId = hex.Length % 2 == 0 && hex.All(Uri.IsHexDigit);","typeGuard":null,"tryCatchPattern":"try\n{\n    var bytes = converter.ToProtobuf(json);\n}\ncatch (ArgumentException ex) when (ex.Message.Contains(\"even length\"))\n{\n    logger.LogWarning(ex, \"Skipping record with malformed hex id.\");\n}","preventionTips":["Zero-pad hex ids to their fixed widths (32 chars trace id, 16 chars span id).","Never hand-edit serialized telemetry JSON.","Add a round-trip test: serialize a known id and deserialize it back."],"tags":["hex","serialization","protobuf","otlp","ids"],"backgroundTag":"invalid-argument-format","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}