Skip to content

Include pushgateway response body in HTTP error messages - #1198

Open
aviseth wants to merge 1 commit into
prometheus:masterfrom
aviseth:fix/issue-96
Open

Include pushgateway response body in HTTP error messages#1198
aviseth wants to merge 1 commit into
prometheus:masterfrom
aviseth:fix/issue-96

Conversation

@aviseth

@aviseth aviseth commented Aug 10, 2026

Copy link
Copy Markdown

@csmarchbanks this is #96, which has been open a long time, so tell me if it is no longer wanted and I will close it.

When the pushgateway rejects a push it says why in the response body: text format parsing error in line 3: expected float as value, a complaint about a duplicate metric name, and so on. urllib raises HTTPError from inside opener.open(), and HTTPError.__str__ renders only HTTP Error 500: Internal Server Error, so the explanation is gone by the time the exception reaches the caller. Most code logs the traceback, and with it the one piece of information that would have said what was wrong with the payload.

_make_handler now catches HTTPError, reads the body, and re-raises HTTPError with the body appended to the reason. The type is unchanged, so except HTTPError, except OSError and except IOError call sites keep working and only the message text differs. An empty body re-raises the original untouched. The resp.code >= 400 branch below is only reachable with a base_handler that suppresses urllib's default error handling, but it had the same problem, so it gets the same treatment.

test_push_error_includes_response_body drives a 500 with a body through the TestPushGateway stub handler, which already branches on the request line, and checks that the body text lands in str(exception) and that it is still an HTTPError with .code == 500. Nothing exercised a >= 400 response from the gateway before this.

Two things I did not want to decide on my own.

The body is not truncated. A reverse proxy in front of the gateway can return a full HTML error page, and all of it would end up in the message. I am glad to cap it if you want one, but the limit is your call.

e.read() consumes the response, so the replacement HTTPError is constructed with fp=None. Anyone currently writing except HTTPError as e: e.read() to get at the body would get an empty read instead. The text is in str(e) now, but it is a behaviour change and you may want it handled another way.

Fixes #96

When the pushgateway rejects a push it explains why in the response body,
but urllib's HTTPError only renders "HTTP Error 500: Internal Server
Error", so the diagnostic was discarded before the caller ever saw it.
Read the body in _make_handler and append it to the error message, still
raising an HTTPError so existing except clauses keep working, and include
the body in the OSError raised for handlers that suppress urllib's default
error handling.

Fixes prometheus#96

Signed-off-by: Avi Seth <avi.seth8@gmail.com>
@aviseth

aviseth commented Sep 2, 2026

Copy link
Copy Markdown
Author

Gentle ping — three weeks, and I think the PR may be stuck on something procedural rather than on the change itself.

gh pr checks reports only DCO on this PR. The Actions workflows have never run, which I believe is the first-time-contributor approval gate: without a maintainer clicking "Approve and run workflows" there is no CI signal for you to review against, and the PR sits in UNSTABLE forever. If someone can approve the run, that resolves itself.

Meanwhile I've rebased onto master (2098346) and re-verified locally on Python 3.13: 418 passed, 11 skipped, no new failures. The diff is unchanged in substance — 25 added, 3 removed across prometheus_client/exposition.py and tests/test_exposition.py.

For context, since the issue is old enough that the ask may need restating: #96 has been open since 2016 with help wanted + enhancement and no PR against it. Today a failed push to the pushgateway raises HTTPError carrying only the status line, so the operator sees 400 Bad Request and none of the body the pushgateway actually returned explaining why — which is usually a specific complaint about the metric names or labels. This includes the response body in the message. The new test was confirmed failing against unmodified exposition.py on the exact assertion before the fix.

Happy to adjust the message format if you'd rather it read differently — that's the only real design choice in here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Print out response body when a 500 Internal Server Error is received from the pushgateway

1 participant