postgrest: count="planned" on insert, update, delete or rpc raises ValueError
Maintainers usually reply within 3 days
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 84/100
Research direction
Start in src/postgrest/src/postgrest/base_request_builder.py at APIResponse._get_count_from_content_range_header (around line 252) and trace how execute() uses its result. Use the supplied MockTransport reproduction for a Content-Range: */* response; done means the call returns its data and reports count as None instead of raising a ValueError.
Written by the indexing model from the issue text.
Description
Describe the bug
Passing count="planned" to insert(), upsert(), update(), delete() or rpc() makes execute() raise ValueError: invalid literal for int() with base 10: '*'. For the mutations, PostgREST has already committed the write by then, so the caller gets an exception for a request that succeeded.
PostgREST only computes a total for these requests with count=exact or count=estimated (shouldCount in src/PostgREST/ApiRequest/Preferences.hs). With count=planned it leaves the total unknown and sends it as * (RFC 9110 allows * for an unknown complete length):
insert/upsertanddelete:Content-Range: */*(Response.hs,contentRangeH 1 0 Nothing)update:Content-Range: 0-0/*for one rowrpc:Content-Range: 0-0/*(the function's count query is skipped and there is noEXPLAINfor calls)
PostgREST v11.2.2, the version in src/postgrest/infra, has the same shouldCount logic.
APIResponse._get_count_from_content_range_header (src/postgrest/src/postgrest/base_request_builder.py:252) calls int() on whatever follows the /:
content_range = content_range_header.split("/")
return None if len(content_range) < 2 else int(content_range[1])
Expected: count is None when the server reports the total as unknown, the same as when no total is sent, and data is returned.
Reproduction
from httpx import Client, MockTransport, Response
from postgrest import SyncPostgrestClient
# Stands in for PostgREST's reply to POST /todos with Prefer: count=planned
def handler(request):
return Response(201, json=[{"id": 1}], headers={"Content-Range": "*/*"})
with Client(transport=MockTransport(handler)) as http:
client = SyncPostgrestClient("https://example.com", http_client=http)
client.from_("todos").insert({"id": 1}, count="planned").execute()
# ValueError: invalid literal for int() with base 10: '*'
With a real project, the same path runs on this call. I confirmed the * totals from PostgREST's source, not against a live server:
supabase.table("todos").insert({"task": "x"}, count="planned").execute()
Steps to reproduce
- Call any mutation or
rpc()withcount="planned". execute()raisesValueError. For a mutation, the row is already written.
Library affected
postgrest
Library version
postgrest 2.33.0 (main at 1ef4688)
Python version
3.13.5
- Dominant language
- Python
- Stars
- 2.6k
- Forks
- 608
- Avg merge
- 3d 18h
- Merged PRs (30d)
- 85
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from supabase/supabase-py
-
storage: analytics bucket errors have the code LibraryError and the raw response body instead of the server's messagePossibly taken @1AbdullahButt claimed this 1 day ago. Openbug python storage
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
supabase/supabase-py#1893 ·
Maintainers usually reply within 3 days
-
realtime: on_postgres_changes without schema sends a binding the server rejectsPossibly taken @hsusul claimed this 2 days ago. Openbug python realtime
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
supabase/supabase-py#1883 ·
Maintainers usually reply within 3 days
-
auth: link_identity() mutates the caller's query_params dictPossibly taken @hsusul claimed this 3 days ago. Openauth bug python
Difficulty 1/5 1-3 hours Newbie friendliness 30/100
supabase/supabase-py#1878 ·
Maintainers usually reply within 3 days
-
Realtime: a late leave reply removes the channel that replaced itPossibly taken @hsusul claimed this 3 days ago. Openbug python realtime
Difficulty 2/5 1-3 hours Newbie friendliness 35/100
supabase/supabase-py#1876 ·
Maintainers usually reply within 3 days
-
auth: Timer uses asyncio.iscoroutinefunction (deprecated in 3.14, removed in 3.16)Possibly taken @maoxor claimed this 3 days ago. Openauth bug python
Difficulty 1/5 Under an hour Newbie friendliness 20/100
supabase/supabase-py#1874 ·
Maintainers usually reply within 3 days
All issues in supabase/supabase-py
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 80/100
Imbad0202/academic-research-skills#1039 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
alexgreensh/token-optimizer#297 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
superdesigndev/treg#1343 ·
Maintainers usually reply within 1 day
-
GitHub handle should allow underscoresPossibly taken @PrakarshSrivastav claimed this today. Openbug good-first-issue
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
nf-core/tools#4581 · 1 comment ·
Maintainers usually reply within 1 day
-
good first issue test
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
ANCORATE/EncryptHome#8 ·