Skip to content

fix(api): dispatch native routes by their declared input - #13

Merged
KAJdev merged 2 commits into
mainfrom
zeke/con-2064-validate-native-api-route-signatures-and
Nov 11, 2026
Merged

KAJdev merged 2 commits into
mainfrom
zeke/con-2064-validate-native-api-route-signatures-and

Conversation

@KAJdev

@KAJdev KAJdev commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

Native API requests could fail with HTTP 500 when the supplied body did not match how the handler was called.

@app.api()
class Service:
    @post("/refresh")
    def refresh(self):
        return {"ok": True}

POST /refresh with {} called this handler with an extra argument. A handler requiring body: dict was called without it when the request body was missing or null.

Now native handlers are invoked according to their declared arguments. Body handlers receive one complete JSON object and reject missing, malformed, null, or non-object input with HTTP 422. No-argument handlers do not consume request bodies.

Declaration validation is covered by the companion SDK PR #713. Both releases are needed for the complete contract.

Refs CON-2064.

@KAJdev
KAJdev enabled auto-merge (squash) November 11, 2026 00:49

@justinwlin justinwlin left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix is right and the tests are thorough. Two notes inline, not blocking.

Comment thread src/runpod_sdk_runtime/api/server.py Resolved
else:
signature = inspect.signature(fn)

if signature.parameters:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rule is "any argument means the handler needs a body". It is only correct when the SDK has already refused handlers like this one:

@get("/search")
def search(self, q="hello"):
    return {"q": q}

q has a default, so the handler does not need a body. But this line sees one argument and requires one. A GET has no body, so every call returns 422.

The SDK and this runtime ship separately, so there are four cases for this handler:

SDK Runtime Result
Without runpod/runpod-python#713 Without this PR 200 (today)
With #713 With this PR Refused at import, clear error (intended)
With #713 Without this PR Refused at import, clear error
Without #713 With this PR Loads, then every request returns 422 Field required: body

The last row is the problem. It can happen because the image and pyproject.toml accept any runpod>=2.2.0. The same applies to def opt(self, body=None) with no body, and to a handler behind a decorator without functools.wraps.

Can we release the SDK first, then raise the minimum version here and in docker/api.Dockerfile? After that the last row cannot occur.

@KAJdev
KAJdev merged commit 89addba into main Nov 11, 2026
7 checks passed
@KAJdev
KAJdev deleted the zeke/con-2064-validate-native-api-route-signatures-and branch November 11, 2026 20:36
@KAJdev
KAJdev restored the zeke/con-2064-validate-native-api-route-signatures-and branch November 11, 2026 20:56
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.

2 participants