microsoft/aspire · error

Hex string must have an even length.

Error message

Hex string must have an even length.

What it means

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.

Solutions

  1. 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).
  2. Validate/repair the stored JSON before conversion: pad the odd-length hex value with a leading zero or discard the record.
  3. Check the custom converter/serializer that produced the JSON; ids should be hex-encoded without dropping leading zeros.

Example fix

// before
new Span { SpanId = spanId } // spanId = "abc" (odd length)
// after: pad to fixed width before conversion
var padded = spanId.Length % 2 == 0 ? spanId : "0" + spanId;
new Span { SpanId = padded }
Defensive patterns

Strategy: validation

Validate before calling

// validate hex ids before conversion
bool isValidHexId = hex.Length % 2 == 0 && hex.All(Uri.IsHexDigit);

Try / catch

try
{
    var bytes = converter.ToProtobuf(json);
}
catch (ArgumentException ex) when (ex.Message.Contains("even length"))
{
    logger.LogWarning(ex, "Skipping record with malformed hex id.");
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/9b6dca7dd5a8e8cf. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Dashboard/Otlp/Model/Serialization/OtlpJsonProtobufConverter.cs:871

        return kvList;
    }

    /// <summary>
    /// Converts a hexadecimal string to a ByteString.
    /// </summary>
    /// <param name="hex">The hexadecimal string to convert.</param>
    /// <returns>A ByteString containing the decoded bytes.</returns>
    /// <exception cref="ArgumentException">Thrown when the hex string has an odd length.</exception>
    internal static ByteString HexToByteString(string hex)
    {
        if (string.IsNullOrEmpty(hex))
        {
            return ByteString.Empty;
        }

        if (hex.Length % 2 != 0)
        {
            throw new ArgumentException("Hex string must have an even length.", nameof(hex));
        }

        var hexSpan = hex.AsSpan();
        var bytes = new byte[hex.Length / 2];
        for (var i = 0; i < bytes.Length; i++)
        {
            bytes[i] = byte.Parse(hexSpan.Slice(i * 2, 2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture);
        }
        return ByteString.CopyFrom(bytes);
    }
}

/// <summary>
/// Converts protobuf types to OTLP JSON types.
/// </summary>
internal static class OtlpProtobufToJsonConverter
{
    /// <summary>

View on GitHub (pinned to 25830f84bd)