SixLabors/ImageSharp · error · ArgumentException

Input string length must be a multiple of 2

Error message

Input string length must be a multiple of 2

What it means

HexConverter.HexStringToBytes parses a hex string into bytes and requires the character span length to be even, since every output byte consumes exactly two hex characters. An odd-length input cannot be parsed unambiguously, so an ArgumentException naming the 'chars' parameter is thrown.

Solutions

  1. Validate/normalize the hex string to an even length before calling (pad a leading 0 or reject the input).
  2. Fix the slicing logic that produced the odd-length span.
  3. Catch ArgumentException and report a user-facing invalid-hex-format error.

Example fix

// before
HexConverter.HexStringToBytes("A1B2C".AsSpan(), dest);
// after
var hex = "A1B2C";
if (hex.Length % 2 != 0) hex = "0" + hex; // or throw InvalidDataException
HexConverter.HexStringToBytes(hex.AsSpan(), dest);
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

static bool IsEvenLengthHex(ReadOnlySpan<char> s) => s.Length % 2 == 0;

Try / catch

try { written = HexConverter.HexStringToBytes(hex.AsSpan(), dest); }
catch (ArgumentException ex) { throw new FormatException("Invalid hex string length.", ex); }

Prevention

When it happens

Trigger: Calling HexStringToBytes with a ReadOnlySpan<char> whose Length is odd, e.g. "ABC" or a truncated hex literal produced by string slicing.

Common situations: Hand-copied hex color/identifier strings with a dropped digit; parsing concatenated hex tokens where one is truncated; off-by-one substring offsets.

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 SixLabors/ImageSharp@59ce6af6fc (2026-09-13). Data as JSON: /api/errors/a8659a188d8874d9. Report an issue: GitHub.

Appendix: source

Thrown at src/ImageSharp/Common/Helpers/HexConverter.cs:21

using System.Runtime.CompilerServices;

namespace SixLabors.ImageSharp.Common.Helpers;

internal static class HexConverter
{
    /// <summary>
    /// Parses a hexadecimal string into a byte array without allocations. Throws on non-hexadecimal character.
    /// Adapted from https://source.dot.net/#System.Private.CoreLib/Convert.cs,c9e4fbeaca708991.
    /// </summary>
    /// <param name="chars">The hexadecimal string to parse.</param>
    /// <param name="bytes">The destination for the parsed bytes. Must be at least <paramref name="chars"/>.Length / 2 bytes long.</param>
    /// <returns>The number of bytes written to <paramref name="bytes"/>.</returns>
    public static int HexStringToBytes(ReadOnlySpan<char> chars, Span<byte> bytes)
    {
        if (Numerics.Modulo2(chars.Length) != 0)
        {
            throw new ArgumentException("Input string length must be a multiple of 2", nameof(chars));
        }

        if ((bytes.Length << 1 /* bit-hack for *2 */) < chars.Length)
        {
            throw new ArgumentException("Output span must be at least half the length of the input string");
        }

        // Slightly better performance in the loop below, allows us to skip a bounds check
        // while still supporting output buffers that are larger than necessary
        bytes = bytes[..(chars.Length >> 1)];   // bit-hack for / 2

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static int FromChar(int c)
        {
            // Map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit.
            // This doesn't actually allocate.
            ReadOnlySpan<byte> charToHexLookup =
            [

View on GitHub (pinned to 59ce6af6fc)