The server rejected the bearer token in session.hello. Causes:
- Token mismatch — the token passed to
AuthScheme.Bearerdoesn't match the one registered inBearerVerifieron the server. - Dev-mode verifier —
ArcpServerOptions.defaultsuses a permissive dev-mode verifier. If you've switched to aStaticBearerVerifier, ensure the token is in the map. - Missing token —
AuthScheme.Nonewas used but the server requires a real token.
// Server: configure a specific verifier
open ARCP.Runtime.Auth
let options =
{ ArcpServerOptions.defaults with
BearerVerifier = StaticBearerVerifier(readOnlyDict [ "my-token", "user-1" ]) }The session.hello envelope failed validation. Common causes:
Client.NameorClient.Versionis empty.Capabilities.Encodingslist is empty — must include"json".
If ConnectAsync throws with a closed transport rather than an
ARCPError, the transport closed before the handshake completed.
Check that:
- The server side is started before the client calls
ConnectAsync. - The server's
HandleSessionAsyncis awaited on a background task, not blocking the caller.
No agent was registered under the name given in JobSubmitRequest.Agent.
Verify you called server.RegisterAgent("name", handler) before
HandleSessionAsync.
A specific version was requested (e.g. "echo@2.0") but no agent with
that name and version was registered. Check RegisterAgentVersion.
The job's effective lease doesn't cover the requested capability/target. Confirm:
- The
LeaseRequestinJobSubmitRequestincludes the namespace and a glob that covers the target. - The server isn't narrowing the lease in a custom
BearerVerifier.
The job's cost.budget constraint for a currency was consumed. The
agent must call EmitMetricAsync with the correct currency and amount.
Check the LeaseConstraints.Budgets map in the submit request.
LeaseConstraints.ExpiresAt was reached before the job completed.
Either extend the deadline or remove lease_expires_at from the
feature set if you don't need expiry enforcement.
If the agent calls BeginStreamingResult() but doesn't complete all
chunks, handle.Result blocks indefinitely. Ensure the agent calls
CompleteStreamingResultAsync (or the finalizing overload of
EmitResultChunkAsync) to close the stream.
The chunk assembler in ArcpClient reorders by event_seq before
reassembly. If events arrive with non-monotonic sequences, check the
transport for packet re-ordering or dropped frames.
Auto-ack is enabled by default (AutoAckOptions.defaults) and fires
at 32 events or 250 ms, whichever comes first. If you observe the
server blocking due to back-pressure, either:
- Lower
EveryEventsor shortenIntervalinAutoAckOptions. - Process events faster inside your consumer loop.
- Set
AutoAckthresholds to effectively infinite values and callAckAsyncmanually after processing.
The endpoint received a non-WebSocket HTTP request. Ensure the client
sends a proper WebSocket upgrade. For curl testing, use websocat
or the arcp send CLI.
Confirm app.UseWebSockets() is called before app.MapArcp(...).
Without UseWebSockets, the middleware stack rejects the upgrade.
Run the full test suite to confirm nothing is broken after local changes:
dotnet test ARCP.slnxUnit tests cover codec round-trips, lease arithmetic, and feature-set laws. Integration tests boot a paired client + runtime over the in-memory transport and exercise the full job lifecycle.