BugHunt

Shared references: when changing a copy changes the original

Assignment does not copy, a shallow copy still shares its contents, and mutating methods return None.

13 free challenges8 Python5 JavaScript

What is mutation and copying bug?

Two names refer to the same underlying object, so a change through one is visible through the other. Or a method mutates in place and returns nothing, and the nothing gets assigned.

Why it happens

Assignment binds a name; it never copies. A shallow copy duplicates the outer container while its elements still point at the originals, so mutating a nested value is visible through both. Python evaluates default arguments once at definition, so a mutable default is shared by every call. And methods that mutate in place return None by convention, which is easy to assign by accident.

How to recognise it

  • A function that should be read-only changes its argument.
  • Two fresh objects share state.
  • A variable becomes None after a sort or reverse.
  • A list changes length while you iterate it, and elements get skipped.

Errors and symptoms this causes

  • editing copy changes original
  • list.sort() returns None
  • shared state between instances
  • mutable default argument
  • removing items skips every other one

How to fix it

Copy explicitly, and match the copy's depth to the depth at which you mutate — a shallow copy protects only the top level. Use None as a default and build the mutable value inside the function. Remember which methods return a new value and which return None: that distinction marks the ones that mutate.

Practise mutation and copying bugs

Working code with one bug in it. Find it, fix it in the browser, and see the explanation. No account needed.

pythonEasy
String casing remains lowercase

shout(text) should return the text in uppercase, so "hello" becomes "HELLO".

Other

pythonEasy
Sorting function returning undefined

sorted_names(names) should return the names in alphabetical order, so sorted_names(["b","a"]) returns ["a","b"].

Other

pythonHard
List cleaner leaves half the items behind

remove_evens(nums) should return the list with all even numbers removed.

Other

pythonHard
Grid clones mutating original data

The function copies a grid, edits the copy, then reports the ORIGINAL's first cell — which should be unchanged. For [[1,2],[3,4]] it returns 1.

Other

javascriptHard
AI Attempt: The English List

ChatGPT was asked to write a function that formats an array of names into an English list (e.g. ['A', 'B', 'C'] -> 'A, B and C'). The function works perfectly the first time you call it. But the developer who used it noticed their data was disappearing.

Other

javascriptHard
Addition formula returning massive numbers

addRounded(a, b) should add two numbers and return the result rounded to two decimal places, as a number. addRounded(1, 2) is 3.

Other

pythonHard
Original config mutated during copy

with_theme(settings, theme) should return a copy with a new theme, leaving the original untouched. It returns "newtheme|originaltheme" so you can see both.

Other

pythonMedium
Default configurations altered unexpectedly

merge_settings(defaults, overrides) should merge overrides on top of defaults without changing the defaults. It returns "mode|numberOfDefaultKeys" so you can see both.

Other

pythonMedium
Math utility exceeding call stack

factorial(n) should return n! (n factorial), e.g. factorial(5) -> 120.

Other

pythonMedium
Total calculation failing with TypeError

total_values(data) should add up a dictionary's values, so total_values({'a': 1, 'b': 2}) returns 3.

Other

javascriptMedium
Financial totals slightly inaccurate

addPrices(a, b) should add two prices in dollars and return a value correct to the cent.

Other

javascriptMedium
Deeply nested arrays not flattening

flattenDeep(arr) should flatten a nested array completely, however many levels deep it goes.

Other

javascriptMedium
Formatter leaving unwanted characters

stripDashes(text) should remove every dash, so stripDashes("a-b-c") returns "abc".

Other

Stuck on your own code?

Paste it into the visualizer and watch it run line by line, with every variable at every step. Free, and it runs in your browser.

Other bug patterns