Behaviour
On engine v2, a query shape the planner refuses by design is reported as an internal error. PlanError::Unsupported is documented as "the caller's error, answered as a bad request, never as a planner defect" (crates/omnigraph-planner/src/error.rs), but on the ordinary query path it becomes OmniError::manifest_internal, which the server answers with HTTP 500.
Reproduced on main (b14c22c5) through the CLI, embedded:
set engine = v2;
query q() { match { $p: Person { name: "p1" } $p knows $d } return { $d.slug } order { bm25($d.body, "needle") } limit 1 }
Error:
0: the planner built no plan for this query: {"kind":"unsupported_query","message":"`bm25()` orders `$d`, a traversal destination, which engine v2 does not support; order on the traversal's source binding, or match the destination with search()"}
Mechanism
engine::execute_query calls plan_source::plan_query, which maps every Unrouted reason through no_plan to OmniError::manifest_internal("the planner built no plan for this query: {json}").
ApiError::from_omni answers ManifestErrorKind::Internal with HTTP 500.
- Only
plan_source::explain_query (the explain statement and Session::query_inspected, which the GQT runner uses) maps Unrouted::UnsupportedQuery to OmniError::manifest, a bad request. The logic-test case v2/planner/search_order_on_an_unranked_binding_and_a_destination.gqt therefore passes on the inspected path while the ordinary path returns 500.
Expected
A refusal by design is a bad request on every path, carrying a typed diagnostic (code, stage, what was refused, one fix), as RFC 0047's diagnostics contract specifies for compiler refusals. no_plan stays for genuine planner defects. The fix text here should also name the fix that works: the example's fix ("order on the traversal's source binding") does not answer the query the user wrote.
Behaviour
On engine v2, a query shape the planner refuses by design is reported as an internal error.
PlanError::Unsupportedis documented as "the caller's error, answered as a bad request, never as a planner defect" (crates/omnigraph-planner/src/error.rs), but on the ordinary query path it becomesOmniError::manifest_internal, which the server answers with HTTP 500.Reproduced on
main(b14c22c5) through the CLI, embedded:Mechanism
engine::execute_querycallsplan_source::plan_query, which maps everyUnroutedreason throughno_plantoOmniError::manifest_internal("the planner built no plan for this query: {json}").ApiError::from_omnianswersManifestErrorKind::Internalwith HTTP 500.plan_source::explain_query(theexplainstatement andSession::query_inspected, which the GQT runner uses) mapsUnrouted::UnsupportedQuerytoOmniError::manifest, a bad request. The logic-test casev2/planner/search_order_on_an_unranked_binding_and_a_destination.gqttherefore passes on the inspected path while the ordinary path returns 500.Expected
A refusal by design is a bad request on every path, carrying a typed diagnostic (code, stage, what was refused, one fix), as RFC 0047's diagnostics contract specifies for compiler refusals.
no_planstays for genuine planner defects. The fix text here should also name the fix that works: the example's fix ("order on the traversal's source binding") does not answer the query the user wrote.