Join our Newsletter — 33% off our NHI Course
Home› FAQ› Foundations & NHI Taxonomy› How should Python developers avoid bugs caused by…
Foundations & NHI Taxonomy

How should Python developers avoid bugs caused by mutable default arguments in function parameters?

← Back to all FAQ
By NHI Mgmt Group Editorial Team Updated October 31, 2026 Domain: Foundations & NHI Taxonomy

Use immutable sentinels, usually None, for defaults that might otherwise hold mutable state. Then create a fresh list or dictionary inside the function when no value is passed. This prevents values from persisting across calls, which is the source of the bug. It also makes the function’s behavior predictable and easier to test.

Why Mutable Default Arguments Persist Across Calls

Python evaluates default parameter values once, when the function is defined, not each time it is called. If that default is a list, dictionary, or another mutable object, later calls reuse the same object. That is why state appears to “leak” between calls, even though the function signature looks harmless.

The key failure mode is hidden shared state. A function that appends to a default list or writes into a default dictionary is mutating an object that survives beyond a single invocation. That makes behavior depend on call history, which is especially confusing during debugging, refactoring, and unit testing.

A fresh object should be created inside the function when no argument is provided. In practice, that means using a sentinel such as None in the signature, then assigning a new list or dict at runtime. This keeps the default value stable while ensuring each call gets its own working data.

Safer Patterns for Function Defaults

The safest pattern is to treat the parameter default as a signal, not as storage. If the caller passes a value, use it; if not, build a new container inside the function. That approach avoids accidental reuse and keeps the function’s behavior tied to the current call rather than earlier ones.

It also helps to choose immutable defaults whenever a real default value exists. Numbers, strings, tuples, and None are straightforward because they cannot be mutated in place. If you need a mutable result, construct it during execution instead of at definition time.

For more implementation guidance on designing predictable Python code and avoiding common security-adjacent mistakes in handling state, the OWASP Cheat Sheet Series is a useful reference point.

When the function is part of a public API, document whether the parameter is optional and whether the function copies or mutates caller-provided data. That distinction matters because a “safe” default can still become surprising if the function mutates the object the caller supplied.

How to Spot the Bug During Review and Testing

Look for any function definition that uses a list, dict, or set directly in the parameter list. Those are the classic cases, but the same issue can appear with custom mutable objects. The review question is simple: will this object be reused across calls if no argument is provided?

Tests should call the function more than once with no argument and confirm that the second call starts clean. A single-call test can miss the bug entirely, because the problem only shows up after prior state has accumulated. This is why the defect often survives basic smoke testing.

When a function is meant to accumulate values, make that behavior explicit with a caller-owned container or a clearly documented lifecycle. Ambiguous defaults make the code harder to reason about than an explicit object passed in by the caller.

Standards & Framework Alignment

This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.

OWASP ASVS, CIS Controls v8 and OWASP SAMM set the governance and control requirements practitioners need to meet.

FrameworkControl / ReferenceRelevance
OWASP ASVSV15 — Secure Coding and ArchitectureMutable defaults are a secure-coding defect in function design.
Recommendation — Use V15 to review function state handling and eliminate hidden shared mutable defaults.
CIS Controls v8CIS-16 — Application Software SecurityThis bug is a preventable software defect that code review and testing should catch.
Recommendation — Apply CIS-16 to test for reusable state and ban mutable defaults in reviewed code.
OWASP SAMMSAMM — Design / ConstructionThe issue is a coding practice problem that belongs in secure design and implementation maturity.
Recommendation — Use SAMM to institutionalise review rules that prevent hidden shared state in functions.

Practitioner Guidance

What to verify: Check every function signature for mutable defaults and confirm that repeated calls do not reuse the same internal object. Pay particular attention to helper functions that look innocuous but are called many times in loops, request handlers, or test fixtures.

Common mistake: Replacing a mutable default with another mutable object just because it feels cleaner. The correct fix is not “different mutable default”, it is “no shared mutable default state”.

Decision rule: If the default value exists only to mean “nothing was passed”, use None or another immutable sentinel and create the container inside the function. If the function truly needs persistent state, make that state explicit in the design rather than hiding it in a parameter default.

Practitioner takeaway: The real goal is predictable call-by-call behavior, so the safest code makes state creation explicit and shared mutation impossible by default.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Reviewed and updated by the NHIMG editorial team on October 31, 2026.
NHI Mgmt Group — the #1 independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org