jstedfast/MailKit · error · ArgumentOutOfRangeException

outputOffset

Error message

outputOffset

What it means

TransformBlock throws ArgumentOutOfRangeException named "outputOffset" when an outputBuffer is supplied and outputOffset is negative or greater than outputBuffer.Length - inputCount. Because TransformBlock copies inputCount bytes into outputBuffer at outputOffset, the destination region must fit entirely inside the output array.

Solutions

  1. Allocate outputBuffer at least inputCount bytes and pass outputOffset 0 when hashing-only.
  2. Validate: 0 <= outputOffset && outputOffset <= outputBuffer.Length - inputCount before the call.
  3. If no copy-back is needed, pass null for outputBuffer — the check is skipped entirely.
  4. Recompute per-block output offsets (advance by inputCount each iteration).

Example fix

// before
transform.TransformBlock(input, 0, input.Length, output, outPos); // outPos + input.Length > output.Length
// after
int outPos = 0;
transform.TransformBlock(input, 0, input.Length, output, outPos);
outPos += input.Length;
Defensive patterns

Strategy: validation

Validate before calling

if (outputBuffer != null && (outputOffset < 0 || outputOffset > outputBuffer.Length - inputCount))
    throw new ArgumentOutOfRangeException(nameof(outputOffset));

Type guard

static bool IsValidOutput(byte[] output, int outputOffset, int inputCount) =>
    output == null || (outputOffset >= 0 && outputOffset <= output.Length - inputCount);

Try / catch

try { transform.TransformBlock(input, 0, inputCount, output, outputOffset); }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "outputOffset") { log.LogError(ex, "Output window [{Off},{Count}] exceeds len {Len}", outputOffset, inputCount, output.Length); throw; }

Prevention

When it happens

Trigger: Calling TransformBlock(input, 0, inputCount, output, badOffset) where badOffset + inputCount > output.Length — e.g. reusing an output offset accumulated across blocks without accounting for inputCount, or an output buffer smaller than the input block.

Common situations: Using TransformBlock as an echo/copy transform (the ICryptoTransform contract) with a mis-sized scratch buffer; ported code assuming outputOffset is always 0 while passing leftover offsets; output buffer allocated for a different block size.

Related errors


AI-assisted analysis of jstedfast/MailKit@9d3859a785 (2026-09-15). Data as JSON: /api/errors/3f3c632d447d37fc. Report an issue: GitHub.

Appendix: source

Thrown at MailKit/Security/Ntlm/MD4.cs:352

			Initialize ();

			return hashValue;
		}

		public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
		{
			if (inputBuffer == null)
				throw new ArgumentNullException (nameof (inputBuffer));

			if (inputOffset < 0 || inputOffset > inputBuffer.Length)
				throw new ArgumentOutOfRangeException (nameof (inputOffset));

			if (inputCount < 0 || inputOffset > inputBuffer.Length - inputCount)
				throw new ArgumentOutOfRangeException (nameof (inputCount));

			if (outputBuffer != null) {
				if (outputOffset < 0 || outputOffset > outputBuffer.Length - inputCount)
					throw new ArgumentOutOfRangeException (nameof (outputOffset));
			}

			HashCore (inputBuffer, inputOffset, inputCount);

			if (outputBuffer != null)
				Buffer.BlockCopy (inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount);

			return inputCount;
		}

		public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount)
		{
			if (inputCount < 0)
				throw new ArgumentOutOfRangeException (nameof (inputCount));

			var outputBuffer = new byte[inputCount];

			// note: other exceptions are handled by Buffer.BlockCopy

View on GitHub (pinned to 9d3859a785)