skip to content

Core Operations

The write side: Add, Modify, ModifyDN, Delete, Compare and Abandon, and the request and response envelope they share. Asked to find out whether you have provisioned a directory or only queried one.

on this pageshow

explore

questions

6

When a directory server answers an LDAP write request, what does the LDAPResult in that reply tell the client?

level: juniorimportance: must knowfreq 48%

answer

  1. one reply shape for most operations
  2. match the reply to the request
  3. a number decides, not the text
  4. zero means applied
  5. how far the name resolved

basics

~20 s

LDAPResult is the shared reply body for Add, Modify, ModifyDN, Delete and Compare. It carries a numeric resultCode such as success(0), a matchedDN showing how far the server resolved the name, and a human-readable diagnosticMessage.

solid answer

~40 s

Every message on an LDAP connection is an `LDAPMessage`: a `messageID`, one protocol operation, and an optional `controls [0]` field. The server repeats the client's `messageID` on the reply, and that is the only correlation the protocol offers, because several operations may be outstanding and a server may answer them in any order. Almost every write reply is the same `LDAPResult`: `resultCode`, `matchedDN`, `diagnosticMessage`, and `referral [3]` when the server does not hold that part of the tree. `success (0)` means applied; anything else is a verdict — `entryAlreadyExists (68)`, `noSuchObject (32)`, `insufficientAccessRights (50)`, `unwillingToPerform (53)`. Branch on `resultCode`; `diagnosticMessage` is free text for a human and may be empty.

code

asn1 · 14 lines
asn1
LDAPMessage ::= SEQUENCE {
     messageID      MessageID,
     protocolOp     CHOICE { ... one operation ... },
     controls       [0] Controls OPTIONAL }

LDAPResult ::= SEQUENCE {
     resultCode         ENUMERATED {
          success              (0),
          noSuchObject         (32),
          entryAlreadyExists   (68),
          ... },
     matchedDN          LDAPDN,
     diagnosticMessage  LDAPString,
     referral           [3] Referral OPTIONAL }

go deeper

for a junior

Recall that the reply to a write is a result code plus a little context, not an exception and not the entry. Know that success is zero and that everything else is the server's reason.

for a middle

Explain why messageID exists at all: several operations can be in flight on one connection and the server may answer out of order. Explain what matchedDN adds on a name-resolution failure.

for a senior

Show that you read matchedDN in production triage, that you log diagnosticMessage without parsing it, and that you know a sequence of write requests is not a transaction even when each one succeeds.

for a principal

Frame the tradeoff of a uniform result envelope: one error path for every operation is cheap to implement and hard to make specific, so client error taxonomies must be built on the codes rather than the prose.

## Every message travels in the same envelope An LDAP connection carries `LDAPMessage` structures in both directions. Each one holds a **`messageID`** chosen by the client, exactly one protocol operation — an `AddRequest`, a `ModifyRequest`, a `ModifyDNRequest`, a `DelRequest`, a `CompareRequest` — and an optional `controls [0]` field. The server repeats the client's `messageID` on every message it sends in answer. That repetition is the **only** correlation the protocol gives you. A client may have several operations outstanding on one connection at the same time, and the server is free to answer them in whatever order it finishes them. Pairing a reply with a request by arrival order is a defect that behaves perfectly on a quiet test directory and misattributes results the first time two writes overlap. ## Almost every reply is the same structure The write operations do not each define a bespoke response body. Add, Modify, ModifyDN, Delete and Compare all answer with the same `LDAPResult` fields: - **`resultCode`** — an enumerated verdict. `success (0)` means the server applied the operation; every other value is the server saying why it did not. - **`matchedDN`** — filled in when the failure was one of *name resolution*. It names the deepest existing entry the server managed to resolve from the DN the client supplied. - **`diagnosticMessage`** — free text intended for a human reader. It may be an empty string, its wording differs between implementations, and nothing in the protocol constrains it. - **`referral [3]`** — present when the `resultCode` is `referral (10)`, meaning this server does not hold the named part of the tree and is pointing elsewhere. The consequence for application code is that error handling is uniform. One function reads a reply, switches on `resultCode`, and never needs to know which of the five write operations produced it. ## Reading the resultCode | resultCode | what the server is saying about a write | |---|---| | `success (0)` | the operation was applied | | `entryAlreadyExists (68)` | an Add named a DN that is already in use | | `noSuchObject (32)` | the named entry, or an ancestor of it, does not exist — read `matchedDN` | | `insufficientAccessRights (50)` | the connection's authenticated identity is not permitted to do this | | `unwillingToPerform (53)` | the server refuses on policy grounds; the request itself was well formed | | `notAllowedOnNonLeaf (66)` | a Delete named an entry that still has subordinate entries | | `compareTrue (6)` / `compareFalse (5)` | a Compare's actual answer, not a failure | The last row is the one that catches people. A Compare never answers `success (0)`; its result *is* the verdict, so code that treats every non-zero `resultCode` as an exception will report an error for a perfectly good comparison. ## matchedDN is a diagnosis, not an echo Suppose a consolidation job adds `cn=Dana Okafor,ou=Staff,dc=riverside,dc=example,dc=org` to a directory where `dc=riverside,dc=example,dc=org` was never created. The reply is `noSuchObject (32)` with `matchedDN` set to `dc=example,dc=org` — the deepest ancestor that does exist. That single field tells the client exactly which level of its assumed tree is missing, which turns a retry loop into a one-line fix: create the intermediate entries first. A client that logs only the `resultCode` throws that information away. ## What a success does and does not promise 1. **A single Modify is applied as one unit.** All of its changes take effect or none do, so a `success (0)` on a Modify means the whole change list landed. 2. **A sequence of operations is not a transaction.** Three separate requests that must all land are three separate verdicts, and the base protocol offers nothing that ties them together. A client that fails half way must be able to re-run its own work. 3. **Success means the server accepted the change as it understood it.** Values are stored under the attribute's own rules, so what comes back on a later read need not be byte-identical to what the client sent — success is a statement about acceptance, not about formatting. The practical discipline is short: correlate on `messageID`, branch on `resultCode`, read `matchedDN` when a name failed to resolve, and log `diagnosticMessage` for the human who will read the ticket — never parse it.

  • An Add fails because the parent entry does not exist. What is in matchedDN?
    The deepest existing ancestor of the DN the client named. With `noSuchObject (32)` on an Add under `dc=riverside,dc=example,dc=org`, a `matchedDN` of `dc=example,dc=org` says the intermediate entry is the missing level. The fix is to create it, not to retry the same request.
  • Can a non-zero resultCode ever mean part of a write was applied?
    Not within one Modify: its change list is applied as a single unit, so a failure leaves the entry untouched. Across requests it is different — a client that sends an Add and then two Modifies has three independent verdicts and no transaction joining them, so it must be able to resume its own sequence.
  • Why should code never branch on diagnosticMessage?
    It is free text for a human reader. The protocol allows it to be empty, and its wording is an implementation choice, so string matching on it breaks when the directory is replaced or upgraded. The `resultCode` is the enumerated value the specification defines for exactly this purpose.

saying these in an interview costs you the question

  • Expects a rejected write to close the connection rather than return a code
  • Branches on the diagnostic message text instead of the resultCode
  • Thinks matchedDN simply echoes the DN the client requested
  • Believes every operation has its own reply fields
  • Pairs replies with requests by the order they arrive
  • Treats compareTrue(6) as an error because it is not zero
open as a page

In an LDAP Modify request, what is the difference between replacing an attribute and deleting one of its values?

level: middleimportance: must knowfreq 58%

basics

~20 s

Replace discards every existing value of the attribute and installs the values listed in the change. Delete removes only the values listed and leaves the rest in place. Replace is an overwrite, never a merge.

open as a page

To move a directory entry under a different parent, what must an LDAP ModifyDN request carry?

level: middleimportance: must knowfreq 52%

basics

~20 s

A ModifyDN request carries the entry's current Distinguished Name, the new RDN, a deleteoldrdn boolean deciding whether the old naming value is discarded, and the optional newSuperior field naming the new parent. Without newSuperior it is a rename in place.

open as a page

A long-running LDAP operation must be called off — what does an AbandonRequest actually guarantee?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Very little. An AbandonRequest names the messageID of an outstanding operation and draws no response of any kind, so the client learns nothing about whether the server stopped, had already finished, or ignored it — and nothing is undone.

open as a page

Why does removing an obsolete branch from a directory take more than one LDAP Delete request?

level: seniorimportance: should knowfreq 38%

basics

~20 s

A Delete request is nothing but one Distinguished Name, and a server refuses to delete an entry that still has subordinates with notAllowedOnNonLeaf (66). Removing a branch is therefore a client-side depth-first walk, one request per entry.

open as a page

Why would a service check one attribute value with an LDAP Compare rather than a search?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

Compare asks one yes-or-no question about one named entry and answers with the resultCode compareTrue(6) or compareFalse(5). It returns no attribute data, so it can confirm a value a caller is not permitted to read and cannot be used to enumerate.

open as a page