{"record":{"id":"31f91457a3b4aeef","repo":"louthy/language-ext","slug":"diff-broken","errorCode":null,"errorMessage":"diff broken","messagePattern":"diff broken","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"LanguageExt.Core/Concurrency/VectorClock/VectorClock.A.cs","lineNumber":331,"sourceCode":"\n    /// <summary>\n    /// If vc2 causes vc1, compute the smallest vc3\n    /// </summary>\n    /// <remarks>Note that the /first/ parameter is the newer vector clock.</remarks>\n    public static Option<VectorClock<A>> diff(VectorClock<A> vc1, VectorClock<A> vc2)\n    {\n        return vc1 == vc2\n                   ? Some(Empty)\n                   : causes(vc2, vc1)\n                       ? Some(combine(diffOne, vc1, vc2))\n                       : None;\n\n        static Option<long> diffOne(A _, Option<long> ox, Option<long> oy) =>\n            (ox.Case, oy.Case) switch\n            {\n                (null, null)     => None,\n                (long x, null)   => Some(x),\n                (null, long)     => throw new InvalidOperationException(\"diff broken\"),\n                (long x, long y) => x == y ? None : Some(x),\n                _                => throw new InvalidOperationException(\"diff broken\")\n            };\n    }\n\n    bool? valid;\n    public bool Valid \n    {\n        get\n        {\n            if (valid.HasValue) return valid.Value;\n            var keys   = Entries.Map(e => e.Item1);\n            var sorted = keys.Sort<OrdDefault<A>, A>() == keys;\n            var distinct = Entries.Distinct<EqTuple2<OrdDefault<A>, TLong, A, long>, (A, long)>().Count ==\n                           Entries.Count;\n            valid = sorted && distinct;\n            return valid.Value;\n        }","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/louthy/language-ext/blob/2f0e3628242889774d4141960a35671a0280051f/LanguageExt.Core/Concurrency/VectorClock/VectorClock.A.cs#L313-L349","documentation":"`VectorClock<A>.diff` compares two vector clocks entry-by-entry via `diffOne`. For each key it expects the pair of clock entries to be (None,None), (Some,None), or (Some,Some); a (None,Some) pair — a key present in the operand clock but absent in the receiver — is structurally impossible for a valid diff, so the library throws `InvalidOperationException(\"diff broken\")` to signal a broken internal invariant.","triggerScenarios":"An entry exists in the second (argument) clock but not in the first (receiver) clock when `Diff` is called — which only happens if a VectorClock was constructed/mutated inconsistently, or with clocks from different generations where the invariant is broken.","commonSituations":"Custom serialization round-trips that drop entries asymmetrically; hand-built or mutated clocks (reflection, partial deserialization) that violate the merge invariant; comparing clocks produced by different, incompatible merge histories.","solutions":["Treat this as a library invariant bug: ensure clocks passed to Diff were only built via `VectorClock` constructors, `Merge`, and `Next`.","Check deserialization code for keys that are dropped asymmetrically between the two clocks.","If reproducible with valid clocks, file a bug with the LanguageExt maintainers including the two clock states."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// ensure clocks share lineage before diffing\nif (!clockA.Valid || !clockB.Valid) throw new InvalidOperationException(\"invalid vector clocks\");","typeGuard":null,"tryCatchPattern":"try { var d = a.Diff(b); }\ncatch (InvalidOperationException ex) when (ex.Message == \"diff broken\") { /* rebuild clocks from source events */ }","preventionTips":["Build clocks only via VectorClock constructors, Merge and Next.","Avoid custom serialization; if needed, round-trip test it against Diff.","Never mutate clock internals with reflection or unsafe casts."],"tags":["vector-clock","invariant","concurrency","csharp"],"backgroundTag":"internal-invariant-violation","analyzedSha":"2f0e3628242889774d4141960a35671a0280051f","analyzedAt":"2026-09-15T03:31:55.716Z","contentChangedAt":"2026-09-15T03:31:55.716Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}