o-5003 was never assigned a dasher. An AI agent holds that symptom and
nothing else: no trace id, no stack trace, no idea which function decided. This page is one real
pass through the tools, against the demo service’s own run of assign o-5003, with the bytes and
tokens each response cost on that run.
The order of the calls is the point. Each one narrows, and the agent pays only for what it opens.
1. Find the trace
vexlo_search_traces with team: ["demo"], service: ["delivery-service"], and
hasError: true. One row comes back:
hasError: true is what finds this trace. root_status is ok: the request itself returned
normally, because assign_dasher caught the rejection and answered with it. A search for
rootStatus: "error" would have come back empty, and an agent that stopped there would have
concluded nothing went wrong. error_step_count: 1 says exactly one step inside the request
errored, and completeness: "complete" says every record of the trace arrived, so the tree is
whole before anything is read.
2. Read the shape
vexlo_get_trace with that traceId. Ten steps, each with its attempts, timing, and slot
previews. The one errored step and the envelope around it:
DasherSelection.pick raised delivery.errors.NoEligibleDasherError on zone SF-94110. Its
two children, DasherSelection.candidates and DasherSelection.in_zone, both have
status: "ok", and their output previews in the omitted lines read []. Nothing crashed
below pick: the functions that look for dashers ran and found none. The shape has already
moved the question from “what broke” to “why was that list empty”, and the coverage block says
no record was lost or shed, so no step is missing from that conclusion.
3. Read the error
Previews cut at 64 code points, so the error message is still half-read.vexlo_get_values with
path: ["AABA"] returns both of that step’s slots in full:
pick was called with one argument, zone.
4. Confirm the empty list
The claim left to check is that no dasher works that zone, as the code saw it.vexlo_get_values with path: ["AABAAAI"], the in_zone step pick called:
in_zone("SF-94110") returned an empty list. The order was rejected because the zone had no
working dasher, not because pick chose wrongly. Both slots read state: "pending" and still
carry a value, which is why the rule is to branch on whether value is present.
What the pass cost
Measured on this run, over the one-line text each tool returned; the blocks above arejq .
renderings of it.
The shape is 81% of it. An agent that trusted the
[] previews in step 2 and stopped there
would have paid 1,876 tokens for the same conclusion with weaker evidence. The one-time startup
handshake, 224 tokens, is not in the table: startup checks and exit
codes covers it.
Four rules this pass follows
Callvexlo_check_coverage before reading an empty result as “nothing happened”. A misspelled
service name and a real, empty answer look identical everywhere else. On this server it costs
171 bytes and 65 tokens, and it names the service and environment that actually exist.
Call vexlo_list_tags before filtering by tag. It is the cheapest call on the surface, 117
bytes and 34 tokens here, and it is what tells an agent the key is order_id rather than
order or orderId.
Call vexlo_check_trace_status for ids you already hold. One id costs 490 bytes and 173 tokens
here, the same as re-running the search above, because what comes back is the same row. The
saving is against a search that is not already narrowed to one: the
ladder measures the default ten-trace page at 1,473 tokens.
Call vexlo_find_related_traces to follow one request past this trace. On this trace it
returns no siblings for 180 bytes and 69 tokens, because no other trace carries
order_id: o-5003. Service plays no part in the query, which is keyed on team plus tag key and
value: the same call on one of this team’s three o-5007 traces, all of them this one service,
returns the other two for 523 bytes and 215 tokens. It reports a shared tag, never a call.