Skip to content

Case 19 โ€” Enum Member Removed

Field Value
Verdict ๐Ÿ”ด BREAKING
Category Breaking
Platforms Linux, macOS, Windows
Flags ABI break, API break
Detected ChangeKinds enum_member_removed
Source files browse on GitHub

Verdict: ๐Ÿ”ด BREAKING Verdict detail: ABI break (symbol removed) + API break (enum value missing from header) abicheck verdict: BREAKING

What changes

Version Definition
v1 enum Status { OK = 0, ERROR = 1, FOO = 2 };
v2 enum Status { OK = 0, ERROR = 1 };

What breaks at binary level

Removing an enum member invalidates any code that uses that member's numeric value. Existing binaries that were compiled with FOO = 2 may store, transmit, or switch on that value. The new library no longer defines that value as a valid member of the enum.

This is a semantic ABI break: while the remaining values (OK, ERROR) are still at the same numeric positions, the removed value FOO = 2 becomes undefined in the new version. Persisted data, protocol messages, or configuration files that contain the removed value will be misinterpreted.

Consumer impact

/* consumer compiled against v1 */
enum Status s = FOO;  /* s = 2 */
write_to_file(s);

/* later, same consumer reads back value 2 with v2 library */
/* library has no FOO โ€” value 2 is undefined behavior */

Mitigation

  • Never remove released enum members; mark them as deprecated instead.
  • Map legacy values deliberately in deserialization/protocol handling.
  • Use explicit sentinel values (e.g., STATUS_MAX) to define valid ranges.

Code diff

-enum Status { OK = 0, ERROR = 1, FOO = 2 };
+enum Status { OK = 0, ERROR = 1 };

Real Failure Demo

Severity: CRITICAL

Scenario: app compiled with old header (has FOO=2) calls library that returns value 2. With v2, FOO is removed โ€” the value 2 is undefined.

# Build old lib + app
gcc -shared -fPIC -g old/lib.c -Iold -o libstatus.so
gcc -g app.c -Iold -L. -lstatus -Wl,-rpath,. -o app
./app
# โ†’ FOO

# Swap in new lib (FOO removed, but still returns integer 2)
gcc -shared -fPIC -g new/lib.c -Inew -o libstatus.so
./app
# โ†’ FOO    โ† prints "FOO" because value 2 still matches the compiled switch case
#            but in new headers this value is UNDEFINED โ€” semantic break
# Any consumer recompiled against new headers would get no FOO case โ†’ "UNKNOWN: 2"

Why CRITICAL: Old binaries still "work" but carry a time bomb โ€” any serialized, stored, or transmitted value of FOO (integer 2) is now undefined in the new API. Recompiled consumers get no FOO case and fall through to default, silently mishandling the value.

Why runtime result may differ from verdict

Enum value removal: binary compat (integers same), semantic/protocol break

References


Source files

See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.