Skip to content

@filter decorator returns empty body {} when combined with serializer_json(auto_unbox=TRUE) in 1.3.3 #1022

Description

@mrcanofcatfood

Environment

  • Plumber 1.3.3 (installed via CRAN snapshot 2026-07-12)
  • R 4.4.2
  • Reproduced inside Docker container in CI

Symptom

GET /health returns HTTP 200 with body {} (empty JSON object)
when the route is registered alongside an @filter decorator that
mutates res$status/res$body for rejections.

Expected: populated JSON body, e.g.
{"status":"ok","active_runs":0,"max_concurrent_runs":2,"memory_gb":6}

The response body becomes the empty object {} rather than the
serialized return value of the route handler.

Reproduction pattern

A router where:

  1. A @filter decorator exists. The filter:
    • Inspects req$PATH_INFO
    • Returns NULL for open endpoints (e.g. /health)
    • Sets res$status and res$body and returns for auth failures
  2. A route handler returns a normal R list
  3. pr$setSerializer(serializer_json(auto_unbox = TRUE, na = "null")) is applied globally

Working workaround

Replace @filter with plumber::pr_hook(pr, "preroute", ...):

  • On rejection: res$status <- ...; res$body <- ...; return(FALSE)
  • On open endpoints: return(NULL) (continue chain)
  • On success: return(NULL) (continue chain)

This pattern returns a populated response body in 1.3.3.

Hypothesis

The @filter decorator path appears to interact poorly with
serializer_json(auto_unbox = TRUE, na = "null") in 1.3.3 when the
filter mutates res$body. Possibly a serializer double-application
or a C++ callback bug in invokeCppCallback. The preroute hook path
does not appear to share the bug.

Observed in

https://github.com/unstable-branch/sdm-dashboard

Notes

We did not narrow this down to a minimal Plumber-only repro. Our
production setup also involves:

  • A Postgres connection pool at startup
  • The serializer_unboxed_json family
  • OpenAPI docs toggled on via PLUMBER_DOCS_ENABLED=true
  • An auth gate that runs on every request

All four of these may be required to trigger the bug. Happy to provide
more context or test patches if useful.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    ai-triage:doneMarks an issue whose AI triage workflow is complete.needs reprexMissing runnable minimal code, app, data, or steps.priority: mediumValid bug or well-defined request with moderate impact or a workaround.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions