{"record":{"id":"4ca33fe5d6988743","repo":"nats-io/nats-server","slug":"dn-ended-with-incomplete-type-value-pair","errorCode":null,"errorMessage":"DN ended with incomplete type, value pair","messagePattern":"DN ended with incomplete type, value pair","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/ldap/dn.go","lineNumber":208,"sourceCode":"\t\t\t\trdn.Attributes = make([]*AttributeTypeAndValue, 0)\n\t\t\t}\n\t\tcase char == ' ' && buffer.Len() == 0:\n\t\t\t// ignore unescaped leading spaces\n\t\t\tcontinue\n\t\tdefault:\n\t\t\tif char == ' ' {\n\t\t\t\t// Track unescaped spaces in case they are trailing and we need to remove them\n\t\t\t\tunescapedTrailingSpaces++\n\t\t\t} else {\n\t\t\t\t// Reset if we see a non-space char\n\t\t\t\tunescapedTrailingSpaces = 0\n\t\t\t}\n\t\t\tbuffer.WriteByte(char)\n\t\t}\n\t}\n\tif buffer.Len() > 0 {\n\t\tif len(attribute.Type) == 0 {\n\t\t\treturn nil, errors.New(\"DN ended with incomplete type, value pair\")\n\t\t}\n\t\tattribute.Value = stringFromBuffer()\n\t\trdn.Attributes = append(rdn.Attributes, attribute)\n\t\tdn.RDNs = append(dn.RDNs, rdn)\n\t}\n\treturn dn, nil\n}\n\n// Equal returns true if the DNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).\n// Returns true if they have the same number of relative distinguished names\n// and corresponding relative distinguished names (by position) are the same.\nfunc (d *DN) Equal(other *DN) bool {\n\tif len(d.RDNs) != len(other.RDNs) {\n\t\treturn false\n\t}\n\tfor i := range d.RDNs {\n\t\tif !d.RDNs[i].Equal(other.RDNs[i]) {\n\t\t\treturn false","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/internal/ldap/dn.go#L190-L226","documentation":"Returned by consumerMemStore.Update when AckFloor.Stream exceeds Delivered.Stream. Stream-wise ack floor must always be <= delivered stream sequence; a state violating this is rejected outright. Same invariant family as the consumer ack floor check, but on the stream sequence namespace.","triggerScenarios":"Calling consumerMemStore.Update(state) where state.AckFloor.Stream > state.Delivered.Stream.","commonSituations":"Restoring/migrating consumer state from a snapshot with mismatched sequences; concurrent updates applied out of order; bugs in ack-floor advancement logic.","solutions":["Fix the code advancing the ack floor so it never exceeds delivered stream sequence","Clamp AckFloor.Stream to Delivered.Stream before Update","Re-derive state from stored message sequences if it came from a suspect source"],"exampleFix":"// before\nst.AckFloor.Stream = 500; st.Delivered.Stream = 400\nconsumer.Update(st)\n// after\nif st.AckFloor.Stream > st.Delivered.Stream {\n    st.AckFloor.Stream = st.Delivered.Stream\n}\nconsumer.Update(st)","handlingStrategy":"validation","validationCode":"if st.AckFloor.Stream > st.Delivered.Stream {\n    st.AckFloor.Stream = st.Delivered.Stream // clamp\n}\nconsumer.Update(st)","typeGuard":null,"tryCatchPattern":"if err := consumer.Update(st); err != nil {\n    if strings.Contains(err.Error(), \"bad ack floor for stream\") {\n        st.AckFloor.Stream = st.Delivered.Stream\n        err = consumer.Update(st)\n    }\n}","preventionTips":["Never compute ack floor and delivered sequences independently","Validate restored/migrated states before applying","Use min()/clamping when merging partial state updates"],"tags":["jetstream","consumer-state","validation"],"backgroundTag":"invalid-ack-floor","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}