{"record":{"id":"3d3dfc65e0bdfe78","repo":"jstedfast/MailKit","slug":"no-hash-value-computed-md4","errorCode":null,"errorMessage":"No hash value computed.","messagePattern":"No hash value computed\\.","errorType":"validation","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"MailKit/Security/Ntlm/MD4.cs","lineNumber":79,"sourceCode":"\t\t\tstate = new uint[4];\n\t\t\tcount = new uint[2];\n\n\t\t\t// temporary buffer in MD4Transform that we don't want to allocate on each iteration\n\t\t\tx = new uint[16];\n\n\t\t\t// the initialize our context\n\t\t\tInitialize ();\n\t\t}\n\n\t\t~MD4 ()\n\t\t{\n\t\t\tDispose (false);\n\t\t}\n\n\t\tpublic byte[] Hash {\n\t\t\tget {\n\t\t\t\tif (hashValue == null)\n\t\t\t\t\tthrow new InvalidOperationException (\"No hash value computed.\");\n\n\t\t\t\treturn hashValue;\n\t\t\t}\n\t\t}\n\n\t\tvoid HashCore (byte[] block, int offset, int size)\n\t\t{\n\t\t\t// Compute number of bytes mod 64\n\t\t\tint index = (int) ((count[0] >> 3) & 0x3F);\n\n\t\t\t// Update number of bits\n\t\t\tcount[0] += (uint) (size << 3);\n\t\t\tif (count[0] < (size << 3))\n\t\t\t\tcount[1]++;\n\n\t\t\tcount[1] += (uint) (size >> 29);\n\n\t\t\tint partLen = 64 - index;","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/jstedfast/MailKit/blob/9d3859a7855e3e17582c07fd01972b8e262bf176/MailKit/Security/Ntlm/MD4.cs#L61-L97","documentation":"MD4.Hash throws InvalidOperationException('No hash value computed.') when the Hash property is read before any data has been hashed, because hashValue is still null. MailKit's NTLM MD4 lazily stores the digest only after ComputeHash/TransformFinalBlock completes.","triggerScenarios":"Accessing md4.Hash immediately after construction, or after TransformBlock-only usage where TransformFinalBlock was never called.","commonSituations":"Debug/inspection code reading Hash mid-pipeline; incremental hashing that forgot the final TransformFinalBlock call; error paths that abandon hashing then read Hash.","solutions":["Call ComputeHash(...) (or TransformFinalBlock) before reading Hash","For incremental hashing, always finish with TransformFinalBlock","Check hashValue availability or wrap Hash access in try-catch for InvalidOperationException"],"exampleFix":"// before\nvar md4 = new MD4 ();\nmd4.TransformBlock (data, 0, data.Length, null, 0);\nbyte[] h = md4.Hash; // InvalidOperationException\n// after\nvar md4 = new MD4 ();\nmd4.TransformBlock (data, 0, data.Length, null, 0);\nmd4.TransformFinalBlock (Array.Empty<byte> (), 0, 0);\nbyte[] h = md4.Hash;","handlingStrategy":"try-catch","validationCode":"// only read Hash after a hash operation completed:\n// md4.ComputeHash(data) or md4.TransformFinalBlock(...) must have been called","typeGuard":"static bool HasDigest (Func<byte[]?> hashAccessor) => hashAccessor () != null;","tryCatchPattern":"byte[] digest;\ntry {\n    digest = md4.Hash;\n} catch (InvalidOperationException) {\n    digest = md4.ComputeHash (Array.Empty<byte> ());\n}","preventionTips":["Always call TransformFinalBlock after TransformBlock calls","Read Hash only inside a completed using scope","Prefer the one-shot ComputeHash over incremental APIs when data fits in memory"],"tags":["csharp","invalid-operation","hashing","ntlm","md4"],"backgroundTag":"invalid-state-transition","analyzedSha":"9d3859a7855e3e17582c07fd01972b8e262bf176","analyzedAt":"2026-09-15T15:46:11.592Z","contentChangedAt":"2026-09-15T15:46:11.592Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}