Skip to content

fix: prevent RuntimeError missing Absinthe.Phase.Document.Result for custom :result_phase (OPS-3643)#1

Open
palantir-valiot[bot] wants to merge 1 commit into
masterfrom
palantir/OPS-3643-fix-missing-result-phase
Open

fix: prevent RuntimeError missing Absinthe.Phase.Document.Result for custom :result_phase (OPS-3643)#1
palantir-valiot[bot] wants to merge 1 commit into
masterfrom
palantir/OPS-3643-fix-missing-result-phase

Conversation

@palantir-valiot

Copy link
Copy Markdown

Summary

Add support for a :result_phase option to Absinthe.Plug (and a post-pipeline normalization pass) so that replacing Absinthe.Phase.Document.Result (either via the new option or a user :pipeline override) no longer produces RuntimeError: Could not find phase Elixir.Absinthe.Phase.Document.Result when Absinthe phases jump to the result phase on validation/complexity/parse/etc. errors.

The root cause was a mismatch: for_document (and carried options on early phases) always targeted the default Result, while the pipeline list contained a replacement; jumps then did Pipeline.from(remaining, Result) against a list that no longer contained it.

  • TDD: added tests (custom pipeline replace + direct option) exercising the complexity-error path that triggers the jump; they went red with the exact stacktrace, then green.
  • Native support: result_phase is now a first-class raw/init option, passed through, replaced in default_pipeline/2, and the carried options updated.
  • Defensive: Absinthe.Plug.ensure_result_phase_consistent/1 (invoked after document-provider pipeline munging) rewrites :result_phase in any carried opts to whatever *Result phase is actually present in the final list. This makes even naive default_pipeline(...) |> Pipeline.replace(Result, MyOrdered) patterns safe.
  • Also upgraded plug/poison (and transitives) + loosened plug constraint (per "keep dependencies fresh" rule) in same PR.

Files changed: mix.exs, mix.lock, CHANGELOG.md, lib/absinthe/plug.ex:127 (raw_options + docs + default_pipeline + helpers), lib/absinthe/plug/request/query.ex:80 (the ensure call), test/lib/absinthe/plug_test.exs (TDD tests + CustomResult helper + pipeline fn).

Why

OPS-3643 (stitchex-lamosa-gto-prod prod crash, high severity code bug). The error was reported against valiot/absinthe_plug; the trigger was stitchex's StitchexWeb.Plugs.GraphQL.pipeline/2 (and similar patterns elsewhere) that do Absinthe.Plug.default_pipeline(...) |> Absinthe.Pipeline.replace(Absinthe.Phase.Document.Result, OrderedResult) for field-order preservation. Same pattern is used by other consumers for Phoenix result structs etc. The fix belongs in the plug.

See triage: NOTIFY+FIX for identifiable RuntimeError in first-party absinthe_plug.

Test plan

  • Added TDD test first; reproduced exact ** (RuntimeError) Could not find phase Elixir.Absinthe.Phase.Document.Result at plug.ex:run_query + pipeline.from (using @complex_query + max_complexity to force Complexity.Result jump).
  • MIX_ENV=test elixir ... -S mix test test/lib/absinthe/plug_test.exs (with pa for test-only codec) → 30/30 green after fix (including original complexity, new custom-pipeline, and new direct-result_phase cases).
  • mix format mix.exs lib/ test/
  • mix hex.outdated + mix deps.update (plug/poison) + constraint bump in mix.exs; tests still green post-upgrade.
  • git diff inspected (no debug, no scope creep, no empty hunks, only the described files).
  • Manual verification that both result_phase: MyMod in init/1 and post-default replace now result in the custom phase being present in the pipeline list and in all carried phase options.
  • No other valiot/ repo change needed (the crash site was in absinthe_plug; stitchex will just work, and can later simplify).

Closes OPS-3643

….Result RuntimeError on jumps (OPS-3643)

- Add native :result_phase support to Absinthe.Plug (passed via init/raw_options to default_pipeline which replaces and sets option for jumps).
- Add defensive ensure_result_phase_consistent (called post document providers) so custom pipeline replaces of Result also work without mismatch on :result_phase in carried opts.
- TDD: added failing-then-passing tests using custom pipeline override and direct option + complexity error path that triggers jump.
- Bump to 1.4.6, update CHANGELOG.
- Upgrade plug/poison (and transitives) within ranges + loosen plug constraint per keep-deps-fresh rule.

Closes OPS-3643
@linear-code

linear-code Bot commented Jun 3, 2026

Copy link
Copy Markdown

OPS-3643

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.

0 participants