reaching for something that isn't there

Index Out of Bounds & Missing Keys

Arrays, dictionaries, and parsed payloads get reached into positionally, on faith. The slot is empty, the key absent, the array shorter than assumed, and the access fails far from the bad assumption.

An index or key is assumed present; the structure never promised it.

01in the wild

In the wild

The Missing Key

Reaching into a structure for something that isn't there — a key, an array slot, a JSON field.

example.py
ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}

# WRONG: assumes the key exists
ages['Michael']                 # KeyError: 'Michael'

# RIGHT: ask for it safely, or validate the shape first
ages.get('Michael', None)       # None
if 'Michael' in ages:
    use(ages['Michael'])
A literal lookup assumes a shape the data never promised. .get() turns a crash into a value you can handle.
// observed
ages['Michael'] -> KeyError
ages.get('Michael') -> None
example.js
// A real one: trusting the shape of an SNS event
const msg = JSON.parse(event.Records[0].Sns.Message);
// throws if Records is empty, or Sns is missing, or...

// RIGHT: optional chaining + validate before parse
const raw = event?.Records?.[0]?.Sns?.Message;
if (typeof raw !== "string") throw new Error("bad event shape");
const msg = JSON.parse(raw);
Each dot and index is an assumption about a payload you didn't build. Optional chaining plus a type check makes the contract explicit.
// observed
unchecked: TypeError: Cannot read '0' of undefined
checked:   throws 'bad event shape' with context
example.rb
# Brittle parsing chained on positional assumptions
valid = href_attrs.length == 2 &&
        href_attrs[0] == 'href' &&
        href_attrs[1].start_with?('"')   # NoMethodError if [1] is nil

# RIGHT: guard each assumption, or parse with a real library
valid = href_attrs.length == 2 &&
        href_attrs[0] == 'href' &&
        href_attrs[1].to_s.start_with?('"')
Index 1 is assumed present and assumed a String. .to_s makes the type safe; a real HTML parser makes the whole thing safe.
// observed
raw:      NoMethodError on nil[1]
to_s:     false (no crash)

The Empty-Collection Edge

first(), last(), and [0] all assume the collection has a first element.

example.py
def latest(events):
    return events[-1]              # IndexError on []

# RIGHT: handle empty, or use a safe accessor
def latest(events):
    return events[-1] if events else None
Negative indexing doesn't wrap to safety; [-1] on an empty list throws just like [0] does.
// observed
latest([]) -> IndexError
guarded    -> None
example.java
// WRONG: assumes the stream produced something
String first = names.stream().findFirst().get();   // NoSuchElementException

// RIGHT: Optional forces you to handle the empty case
String first = names.stream().findFirst().orElse("(none)");
Optional.get() on an empty Optional throws. orElse / orElseThrow make the empty path a decision, not an accident.
// observed
get() on empty   -> NoSuchElementException
orElse fallback  -> "(none)"
02cross-pollination

Where this compounds

03weakness catalog

Mapped weaknesses (CWE)

On its own, this defect is catalogued by MITRE as one or more of these weaknesses. The exploitable vulnerability usually appears only when it chains or combines with another.