# Proper handling of PyList\_SetItem return value and reference stealing in CPython C extensions

**URL:** <https://discuss.python.org/t/proper-handling-of-pylist-setitem-return-value-and-reference-stealing-in-cpython-c-extensions/105805>\
**Category:** C API\
**Created:** [January 20, 2026, 8:40am UTC](https://discuss.python.org/t/proper-handling-of-pylist-setitem-return-value-and-reference-stealing-in-cpython-c-extensions/105805 "2026-01-20T08:40:59Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![wr-web](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/wr-web/32/30736_2.png) [@wr-web](https://discuss.python.org/u/wr-web)\
**Post date:** [January 20, 2026, 8:40am UTC](https://discuss.python.org/t/proper-handling-of-pylist-setitem-return-value-and-reference-stealing-in-cpython-c-extensions/105805/1 "2026-01-20T08:40:59Z")

</div>

I have observed a recurring pattern in many third‑party CPython extension codes:

```c
if (PyList_SetItem(a, b, item) < 0) {
    Py_DECREF(item);
} else {
    ...
}

```

[CPython PyList\_SetItem Source Code](https://github.com/python/cpython/blob/71cbffde61449a224dffcae937f5f6be4f86ad09/Objects/listobject.c#L453)

As far as I understand, `PyList_SetItem` “steals” a reference to `item`. **According to the CPython source code, even when the function returns an error (value \< 0), it still “steals” the reference to `item`—meaning `item`’s reference count is already decremented internally in the error path.**

If that is the case, then the additional explicit `Py_DECREF(item)` in the user code would cause a double decref. This could lead to the reference count dropping below zero and potentially trigger undefined behavior or memory issues.

My questions are:

1. Is the above pattern actually incorrect?
2. What is the correct way to handle `item` when `PyList_SetItem` fails?
3. If a double decref does occur, what kind of problems might it cause in practice?

I would appreciate clarification from the community or references to the official CPython documentation on this matter.

Thank you!

---

<div class="post-metadata">

**Author:** ![da-woods](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/da-woods/32/7507_2.png) [@da-woods](https://discuss.python.org/u/da-woods)\
**Post date:** [January 20, 2026, 9:53am UTC](https://discuss.python.org/t/proper-handling-of-pylist-setitem-return-value-and-reference-stealing-in-cpython-c-extensions/105805/2 "2026-01-20T09:53:10Z")

</div>

> [@wr-web](#):
>
> Is the above pattern actually incorrect?

Yes

> [@wr-web](#):
>
> What is the correct way to handle `item` when `PyList_SetItem` fails?

Usually you do nothing - you no longer own the reference.

> [@wr-web](#):
>
> If a double decref does occur, what kind of problems might it cause in practice?

It will usually crash. (Although of course SetItem rarely fails because you usually meet all the preconditions. So mostly you don’t see this code-path)

---

<div class="post-metadata">

**Author:** ![barry](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/barry/32/42_2.png) [@barry](https://discuss.python.org/u/barry)\
**Post date:** [January 20, 2026, 4:12pm UTC](https://discuss.python.org/t/proper-handling-of-pylist-setitem-return-value-and-reference-stealing-in-cpython-c-extensions/105805/3 "2026-01-20T16:12:26Z")

</div>

[Documented](https://docs.python.org/3/c-api/list.html#c.PyList_SetItem) behavior.

---

<div class="post-metadata">

**Author:** ![MRAB](https://avatars.discourse-cdn.com/v4/letter/m/e9c0ed/32.png) [@MRAB](https://discuss.python.org/u/MRAB)\
**Post date:** [January 20, 2026, 5:01pm UTC](https://discuss.python.org/t/proper-handling-of-pylist-setitem-return-value-and-reference-stealing-in-cpython-c-extensions/105805/4 "2026-01-20T17:01:07Z")

</div>

There’s generally an expectation that a call will be all-or-nothing, that if the calls fails, nothing will have changed.

`PyList_SetItem` doesn’t follow that expectation.

Yes, it’s documented, but given that the OP has seen many third‑party CPython extensions that get it wrong, maybe the documentation needs to be more explicit.

Instead of:

> This function “steals” a reference to _item_

perhaps:

> This function **always** “steals” a reference to _item_, even on error

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [January 20, 2026, 5:31pm UTC](https://discuss.python.org/t/proper-handling-of-pylist-setitem-return-value-and-reference-stealing-in-cpython-c-extensions/105805/5 "2026-01-20T17:31:53Z")

</div>

> [@barry](#):
>
> [Documented](https://docs.python.org/3/c-api/list.html#c.PyList_SetItem) behavior.

The text at that link doesn’t unambiguously say what happens to the item’s refcount on failure (since the sentence also mentions something that only happens on success). We should document exactly what it does since we can’t possibly change it – we’d break all the existing code that uses it correctly.

I personally think that the current behavior (always DECREF) is the more ergonomic of the two design choices for this API, _given_ the choice of DECREF’ing on success. (The choice to steal is understandable but confusing since so few functions steal an argument – in modern times we probably would have marked this fact in the API name, e.g. PyList\_SetItemStealing. But it’s too late for that and it’s not worth deprecating anything for this small wart.)

---

<div class="post-metadata">

**Author:** ![wr-web](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/wr-web/32/30736_2.png) [@wr-web](https://discuss.python.org/u/wr-web)\
**Post date:** [January 20, 2026, 5:40pm UTC](https://discuss.python.org/t/proper-handling-of-pylist-setitem-return-value-and-reference-stealing-in-cpython-c-extensions/105805/6 "2026-01-20T17:40:25Z")

</div>

> [@](#):
>
> Instead of:
> 
> > This function “steals” a reference to _item_
> 
> perhaps:
> 
> > This function **always** “steals” a reference to _item_, even on error

Agreed,

I’ve noticed that a significant number of developers have made the same mistake. It’s highly likely because the documentation regarding whether the successful execution of this function affects the reference count can be easily misinterpreted.

We can give this a try：

> **[context:global PyList\_Se… - Sourcegraph](https://sourcegraph.com/search?q=context:global+PyList_SetItem.*%3C.*&patternType=regexp&sm=0)**
>
> Search across 2 million+ open source repositories for free. Powered by Sourcegraph Code Search.

---

<div class="post-metadata">

**Author:** ![ngoldbaum](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ngoldbaum/32/12276_2.png) [@ngoldbaum](https://discuss.python.org/u/ngoldbaum)\
**Post date:** [January 20, 2026, 11:34pm UTC](https://discuss.python.org/t/proper-handling-of-pylist-setitem-return-value-and-reference-stealing-in-cpython-c-extensions/105805/7 "2026-01-20T23:34:04Z")

</div>

> [@wr-web](#):
>
> I’ve noticed that a significant number of developers have made the same mistake. It’s highly likely because the documentation regarding whether the successful execution of this function affects the reference count can be easily misinterpreted.

For what it’s worth, if you look in the C error paths for almost any Python C API usage, I bet you’ll find bugs like this. Messing up error handling is one of the most common sources of C API bugs and it’s the number 1 thing I look for in NumPy C code reviews.

Error paths also tend to be poorly tested, since it often requires constructing an elaborate setup to trigger errors. Most people don’t bother, so we have lots of buggy error paths in the ecosystem.

---

<div class="post-metadata">

**Author:** ![encukou](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/encukou/32/2461_2.png) [@encukou](https://discuss.python.org/u/encukou)\
**Post date:** [February 4, 2026, 2:33pm UTC](https://discuss.python.org/t/proper-handling-of-pylist-setitem-return-value-and-reference-stealing-in-cpython-c-extensions/105805/8 "2026-02-04T14:33:01Z")

</div>

I opened an [issue](https://github.com/python/cpython/issues/144473) and [PR](https://github.com/python/cpython/pull/144502).
