C# KeyNotFoundException: common causes
Fix C# KeyNotFoundException from Dictionary lookups by using TryGetValue, GetValueOrDefault, or validating the key before access.
Symptoms
- System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary
- A lookup works for known keys but crashes on missing ones
- The dictionary key came from user input or a config file
Possible causes
- The key is absent and indexer [] is used instead of TryGetValue
- Case sensitivity differs between insert and lookup
- The dictionary was populated from a source missing some keys
- Concurrent modification removed the key between check and access
Diagnosis steps
Log the missing key value
catch (KeyNotFoundException e) { logger.LogError("key {Key}", key); }Interpretation
Seeing the exact key reveals whether it is a case, whitespace, or genuinely absent value.
Inspect dictionary contents around the access
Interpretation
Printing keys shows the expected key is not present, often due to a formatting mismatch.
Switch to a non-throwing lookup
dict.TryGetValue(key, out var value)Interpretation
If TryGetValue returns false, the key is truly absent and the caller must handle it.
Root cause
Dictionary indexer access throws KeyNotFoundException when the key does not exist, unlike some other languages that return a default. The key may be absent because of case sensitivity, whitespace, or because upstream data did not populate it, and the compiler offers no protection for the lookup.
Fix
Use TryGetValue or CollectionExtensions.GetValueOrDefault for optional keys, and validate required keys with a clear domain exception. Match the comparer to the data (StringComparer.OrdinalIgnoreCase when case-insensitive), and normalise keys before insert and lookup so the two agree.
Verify the fix
Run the lookup with the previously missing key; with TryGetValue the code returns the fallback cleanly, and a test asserts the missing-key path no longer throws.
Symptoms
A dictionary lookup throws KeyNotFoundException. It happens only for keys not present in the dictionary, so it survives until real-world data arrives.
Diagnosis
Capture the key and inspect the contents:
try {
var v = dict[key];
} catch (KeyNotFoundException) {
logger.LogError("missing key: [{Key}]", key);
throw;
}
Case or whitespace differences are common: "User vs "user".
Fix
Prefer non-throwing lookups:
if (dict.TryGetValue(key, out var value)) {
return value;
}
return defaultValue;
// or
var v = dict.GetValueOrDefault(key, fallback);
Use a case-insensitive comparer when appropriate:
var map = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
Verification
Call the lookup with the key that used to be missing; no exception is thrown and the fallback is returned. A test asserts the missing-key path.
Choosing Lookups by Contract
The right accessor depends on whether the key is required. A missing key that indicates a programming error should fail fast, so an explicit check with a domain exception is clearer than a bare indexer. A missing key that is normal — an optional config override, a cache miss — should never throw, so TryGetValue or GetValueOrDefault is correct. Mixing the two is what makes KeyNotFoundException reappear: a lookup that once assumed presence keeps throwing when data changes. Normalising keys with a single comparer, and documenting which keys are mandatory, keeps insert and lookup in agreement as the schema evolves.