skip to content

An LDAP search returns noSuchObject(32) with a matchedDN shorter than the base you sent — what does that diagnose?

level: seniorimportance: should knowfreq 34%

answer

  1. read the second field, not just the code
  2. how far down the name resolved
  3. the difference is the broken part
  4. empty means wrong server or suffix
  5. absence of an entry is not proved

basics

~20 s

It names the longest ancestor of your base that does exist, so everything below it in the name is wrong. A matchedDN equal to the suffix means the container is missing or misspelled; an empty one means the server holds no naming context covering that name at all.

solid answer

~40 s

`matchedDN` in the result carries the **lowest entry the server did match** while walking your name from the top down, so the difference between it and the base you sent is exactly the broken part. If you asked for `ou=curatorss,dc=seedbank,dc=example` and `matchedDN` comes back as `dc=seedbank,dc=example`, the suffix is right and the container name is wrong — a typo, a case of the container never having been created, or a client pointed at the wrong subtree. An **empty** `matchedDN` means nothing matched, so the server holds no naming context covering that name: read the root DSE's `namingContexts` before touching anything else. The field is only populated for noSuchObject(32), aliasProblem(33), invalidDNSyntax(34) and aliasDereferencingProblem(36); on other result codes it is empty and carries no signal.

code

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

-- search base sent: uid=hbell,ou=curatorss,dc=seedbank,dc=example
-- resultCode        noSuchObject (32)
-- matchedDN         dc=seedbank,dc=example
-- diagnosticMessage ""

go deeper

for a junior

Know that a failed lookup returns a code plus the part of the name that did resolve, and that comparing the two tells you where the name went wrong.

for a middle

Explain how a server resolves a name top-down, why matchedDN is the lowest entry reached, and how noSuchObject differs from a name that never parsed.

for a senior

Run the diagnosis end to end and state the limit: this code collapses absent, invisible and not-held-here, so never build a deletion decision on it without reproducing the read as an identity that may see the entry.

for a principal

Set the estate's rule for what a negative directory answer is allowed to trigger — an automated lifecycle action driven by a code that also means 'you may not know' is a fleet-wide outage waiting for a permission change.

## One symptom, several causes "The lookup returns nothing" is the most common directory report an engineer receives, and it has at least four distinct causes: the name never parsed, the name is well-formed but no such place exists, the place exists but the filter matched nothing, and the place exists but this connection may not learn that it does. The result carries enough to separate them, and most people never read the second field. ## What `matchedDN` is A result message carries a `resultCode`, a `matchedDN` and a `diagnosticMessage`. When a server resolves a name it walks the tree from the top of the relevant naming context downward. If it runs out of tree before it runs out of name, it answers **noSuchObject(32)** and sets `matchedDN` to **the name of the lowest entry it did reach**. That makes the field a pointer at the exact step that failed: ```pseudocode requested = "uid=hbell,ou=curatorss,dc=seedbank,dc=example" matchedDN = "dc=seedbank,dc=example" => the suffix resolved; "ou=curatorss" did not ``` Read the two names together and the diagnosis is mechanical: 1. **`matchedDN` equals the suffix.** The server holds the right naming context; the container immediately below it is missing or misspelled. Check spelling and case-sensitivity of the *value* — attribute types are case-insensitive, values may not be. 2. **`matchedDN` is one step short of the base.** Everything above resolves and the final container is the problem, which usually means the tree was reorganised or the entry has been moved elsewhere. 3. **`matchedDN` is empty.** Nothing at all matched: this server holds no naming context that covers the name. Read the root DSE's `namingContexts`; you are either on the wrong server or using a suffix from a different environment. One restriction matters: `matchedDN` is populated only for the codes that are about *resolving a name* — noSuchObject(32), aliasProblem(33), invalidDNSyntax(34) and aliasDereferencingProblem(36). For every other result code it is empty, and reading meaning into that emptiness is a mistake. ## The codes a bad name earns | Result code | What it means | Where the fault usually is | |---|---|---| | invalidDNSyntax(34) | the string is not a Distinguished Name at all | the client, which built the name by concatenation and skipped escaping | | noSuchObject(32) | the name parsed, the entry is not there | configuration, a typo, or a tree that was reorganised | | namingViolation(64) | a proposed name breaks the rules for where an entry may sit | an attempt to place an entry, not a read | | aliasProblem(33) | a name resolved onto an alias entry that does not point at a real entry | the alias's `aliasedObjectName`, left dangling | The first two are the pair people confuse. `invalidDNSyntax(34)` happens **before** any lookup: the string failed to parse. `noSuchObject(32)` means the string was a perfectly good name for a place that is not there. ## What the code does not prove This is the senior half of the question. **noSuchObject(32) is not proof that the entry does not exist.** A directory server may answer it where the entry is present but the bound identity may not learn of its existence, because returning "you are not allowed" would itself disclose that something is there. So the honest reading is: - the entry is absent, **or** - the entry is present and this connection is not permitted to know it, **or** - this server does not hold that part of the tree. The practical consequence: before concluding an account has been removed — the "does this person still exist" probe that onboarding and offboarding jobs rely on — reproduce the read as an identity that certainly may see it. An offboarding job that treats noSuchObject(32) as proof of deletion will happily disable everyone the day its own credentials lose read access to a subtree. ## An alias in the middle of the name A name can also resolve onto an **alias** entry — the `alias` object class, whose required `aliasedObjectName` attribute holds another entry's Distinguished Name, giving one entry a second name. If that target no longer exists, resolution fails with aliasProblem(33) rather than plain noSuchObject(32), and `matchedDN` again points at how far the walk got. Whether a search *follows* an alias is a separate control on the search itself, not a property of the name. ## A short diagnostic order 1. Read the `resultCode` and decide: parse failure, or resolution failure? 2. Read `matchedDN` and subtract it from the base you sent — that difference is the broken segment. 3. If `matchedDN` is empty, read the root DSE and compare `namingContexts` with the suffix you configured. 4. Only then consider authorization: repeat the read as an identity that certainly may see the entry. 5. Treat `diagnosticMessage` as a hint for humans, never as something to parse — its text is not specified.

  • What does an empty matchedDN alongside noSuchObject(32) tell you?
    That not one component of your name resolved, so the server holds no naming context covering it. That is a wrong-server or wrong-environment problem rather than a missing container: the suffix you configured belongs somewhere else. Read the root DSE's `namingContexts` and compare. It is the fastest way to catch a client pointed at a test instance with production names, or the reverse.
  • Why is noSuchObject(32) not proof that the entry has been deleted?
    Because a server may return it where the entry exists but the bound identity may not learn of it — answering "insufficient rights" would itself confirm the entry is there. So the code collapses "absent", "invisible to you" and "not held here" into one answer. Any job that treats it as proof of deletion will mass-disable accounts the moment its own read access to a subtree changes.
  • When would namingViolation(64) appear instead?
    On an attempt to place an entry at a name the tree's structure rules do not allow — the proposed name is syntactically fine and the parent may even exist, but that kind of entry may not be named there. It is a write-time answer about where a name is allowed to sit, whereas noSuchObject(32) is a resolution-time answer about a name that leads nowhere.

A courier who cannot find flat 7B leaves a note saying which building they did reach: that is matchedDN — the last part of the address that existed. It does not tell you the flat is empty, only that they got no further, and sometimes the porter has simply been told not to confirm who lives there.

saying these in an interview costs you the question

  • Reads noSuchObject as proof the entry does not exist
  • Takes matchedDN to be the name that was searched for
  • Expects matchedDN populated on every failed result
  • Confuses it with invalidDNSyntax, which is a parse failure
  • Blames credentials before comparing base against matchedDN
  • Parses the diagnostic message text programmatically