/operation/directives.llms.txt
Directives
Every request can carry its own instructions. Add an X-ERPC-* header or a ?param= query string and eRPC instantly shifts routing, caching, integrity checks, or consensus behavior for that call only. No server restart. No config change. These directives can also be pinned as directiveDefaults on any network so every call starts with a sensible baseline.
For production-facing projects, allowClientDirectives can restrict which directives clients may send — block cache-bypass or upstream-pinning headers while keeping integrity directives available.
When eRPC is itself the edge (no trusted proxy in front to strip headers), the restriction can be scoped per caller instead of per project: set allowClientDirectives: "" on the project to deny everyone by default, then re-grant on the individual auth strategies that trusted callers authenticate with:
projects:
- id: main
allowClientDirectives: "" # default: nobody
auth:
strategies:
- type: secret # operators: every directive
allowClientDirectives: "*"
secret: { id: ops, value: ${OPS_TOKEN} }
- type: secret # partners: upstream pinning only
allowClientDirectives: "use-upstream"
secret: { id: partner, value: ${PARTNER_TOKEN} }
- type: jwt # everyone else: inherits "" → denied
jwt: { ... }The grant travels on the authenticated User, so it is only ever issued by the strategy that actually verified the caller's credential. A caller whose identity comes from trustUserIdHeader carries no grant and always falls back to the project value — spoofing the identity header cannot widen directive access.
After each response, eRPC echoes back what actually happened — which upstream won, cache hit or miss, retry and hedge counts — so you always know exactly what the proxy did.
Quick taste
Per request, the way directives are actually used — pin one call to specific upstreams via header or query string:
# via header: only Alchemy upstreams may serve this requestcurl https://rpc.example.com/main/evm/1 \ -H 'X-ERPC-Use-Upstream: alchemy-*' \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'
# same directive as a query parametercurl 'https://rpc.example.com/main/evm/1?use-upstream=alchemy-*' \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'Directives that make sense as a permanent baseline can be pinned per network with
directiveDefaults — headers and query params still override them per request:
projects: - id: main networks: - architecture: evm evm: { chainId: 1 } directiveDefaults: # retry when upstream returns null/empty (e.g. not-yet-indexed block) retryEmpty: true # reject responses whose block number is behind the known tip enforceHighestBlock: trueAgent reference
Copy one of these prompts into your AI agent session (Claude Code, Cursor, …) — each one points the agent at this page's machine-readable reference so it can do the work correctly:
Prompt Example #1: set sane directive defaults for a new network
I'm adding a new EVM network to my eRPC config and want to pin sensible directiveDefaults — retryEmpty, enforceHighestBlock, enforceNonNullTaggedBlocks, and useUpstream routing for archival calls. Read the full reference first: https://docs.erpc.cloud/operation/directives.llms.txt
Prompt Example #2: audit and tighten my existing directive defaults
Review the directiveDefaults blocks in my eRPC config: check which integrity directives are on by default but may be incompatible with non-standard chains (zkSync, Filecoin, Kaia style), flag any retryPending: true set globally (dangerous), and suggest per-network overrides. Read the reference: https://docs.erpc.cloud/operation/directives.llms.txt
Prompt Example #3: pin an archival upstream for indexer traffic
My indexer sends eth_getLogs and eth_getBlockReceipts requests that should always hit an archival upstream tagged family:archival in my eRPC config. Configure directiveDefaults.useUpstream so those calls are pinned to that group and set the network's integrity level for indexing correctness. Reference: https://docs.erpc.cloud/operation/directives.llms.txt
Prompt Example #4: debug why X-ERPC-Retry-Empty is not retrying
My requests have X-ERPC-Retry-Empty: true in the header but eRPC is not retrying empty results. Walk me through why this might happen (boolean parse rules, EmptyResultMaxAttempts cap, gRPC path limitation) and how to verify via the X-ERPC-Attempts response header that retries are actually firing. Work with my existing eRPC config. Reference: https://docs.erpc.cloud/operation/directives.llms.txt
Prompt Example #5: reduce diagnostic header noise in production logs
Our load balancer logs every X-ERPC-Upstreams response header and it's adding megabytes to our access logs. Configure executionHeaders on the server to suppress the per-attempt trace while keeping the counter headers for dashboards. Work with my existing eRPC config. Reference: https://docs.erpc.cloud/operation/directives.llms.txt
Prompt Example #6: lock down client directives on a public-facing project
My eRPC project serves public traffic and I want to prevent clients from sending X-ERPC-Skip-Cache-Read or X-ERPC-Use-Upstream headers to bypass caching or pin upstreams. Configure allowClientDirectives so integrity directives are still allowed but operational ones are blocked. Reference: https://docs.erpc.cloud/operation/directives.llms.txt
Directives — full agent referenceExpand for every option, default, and edge case — or copy this entire section into your AI assistant.
How it works
Parsing pipeline. For every HTTP request eRPC runs three steps. First, ApplyDirectiveDefaults copies any directiveDefaults config block into the request struct (lowest priority). Second, SetAllowClientDirectiveMatcher stores a pre-compiled matcher function from the project-level allowClientDirectives pattern (compiled once at project registration via NewWildcardMatcher). Third, EnrichFromHttp scans all registered header and query names, skipping any directive whose query-param key is rejected by the matcher. If no directives are present it returns immediately after extracting User-Agent — zero allocations, zero locks. When directive inputs are present the struct is cloned before mutation so batch sub-requests that share the same pointer do not race.
Precedence from lowest to highest: directiveDefaults config → HTTP header → URL query parameter. A query-param value always wins over the same header, which always wins over config. ApplyDirectiveDefaults is idempotent — once r.directives is non-nil every subsequent call is a no-op, so per-request overrides can never be clobbered by a second config pass. Source: common/request.go:563-676 (opens in a new tab).
Boolean parsing rule. For every boolean directive header the only truthy value is the exact string "true" (case-insensitive, whitespace stripped). "1" and "yes" are not truthy. X-ERPC-Force-Trace is handled by the tracing subsystem separately and accepts all three. Always use "true" to avoid this asymmetry. Confirmed: common/request_test.go:406-427 (opens in a new tab).
directiveDefaults placement. Settable at projects[].networks[].directiveDefaults (per network) or projects[].networks[].failsafe.directiveDefaults (failsafe scope). Every *bool field has three states: nil (omitted — request field stays at Go zero false), pointer to false (explicitly written), pointer to true. The nil check in ApplyDirectiveDefaults is a "was this field set?" guard, not a multi-level inheritance chain.
gRPC path. EnrichFromHttp is never called on the gRPC path. gRPC callers receive only directiveDefaults; per-request header overrides are not available. Source: erpc/request_processor.go:35-67 (opens in a new tab).
Config schema
Config struct: common/config.go:2105-2152 (opens in a new tab). Applied by ApplyDirectiveDefaults at common/request.go:563-676 (opens in a new tab).
Complete directive registry
| # | HTTP header | Query param | Type | Config field | Default | Effect | Consumed at |
|---|---|---|---|---|---|---|---|
| 1 | X-ERPC-Retry-Empty | retry-empty | bool | retryEmpty | false | Retry on null/empty upstream response. Subject to EmptyResultMaxAttempts cap. | erpc/network_executor.go:427-442 (opens in a new tab) |
| 2 | X-ERPC-Retry-Pending | retry-pending | bool | retryPending | false (struct comment "true by default" is stale) | Retry eth_getTransactionReceipt / eth_getTransactionByHash / eth_getTransactionByBlockHashAndIndex / eth_getTransactionByBlockNumberAndIndex while tx is pending. EmptyResultMaxAttempts cap applies. | erpc/network_executor.go:445-459 (opens in a new tab) |
| 3 | X-ERPC-Skip-Cache-Read | skip-cache-read | string (bool in config YAML normalized to string) | skipCacheRead | "" (no skip) | "true" = skip all; "false"/"" = use cache; other string = connector-ID WildcardMatch pattern. YAML true/false normalized to strings via fmt.Sprintf("%v", v). Header NOT trimmed; query IS trimmed. | common/request.go:898-914 (opens in a new tab) |
| 4 | X-ERPC-Use-Upstream | use-upstream | string | useUpstream | "" (no filter) | Selector matched against upstream id; for purely-positive patterns also against upstream Tags (any tag match wins). Negated patterns (!) match ID only. Header NOT trimmed; query IS trimmed. No match → ErrUpstreamsExhausted. Activates selector-scoped served-tip partitioning. Stateful methods with multiple matching upstreams fail with ErrNotImplemented. | upstream/upstream.go:1526-1534 (opens in a new tab) |
| 5 | X-ERPC-Skip-Interpolation | skip-interpolation | bool | skipInterpolation | false | Suppresses block-tag → hex substitution in forwarded params. Internal block refs still computed/cached. | architecture/evm/json_rpc.go:132 (opens in a new tab) |
| 6 | X-ERPC-Skip-Consensus | skip-consensus | bool | skipConsensus | false | Bypasses consensus branch; uses standard retry(hedge(upstreamSweep)). Retry/hedge/breaker/timeout still apply. | erpc/network_executor.go:179-188 (opens in a new tab) |
| 7 | X-ERPC-Enforce-Highest-Block | enforce-highest-block | bool | enforceHighestBlock | true (SetDefaults) | eth_getBlockByNumber with "latest"/"finalized": re-routes if response is behind known highest block. Skipped for cached responses. | architecture/evm/eth_getBlockByNumber.go:111-170 (opens in a new tab) |
| 8 | X-ERPC-Enforce-GetLogs-Range | enforce-getlogs-range | bool | enforceGetLogsBlockRange | true (SetDefaults) | Pre-forward: rejects eth_getLogs/trace_filter if block range exceeds getLogsMaxAllowedRange. Consumed via legacy EvmIntegrityConfig (not a true per-request override — see Edge cases). | architecture/evm/eth_getLogs.go:280-288 (opens in a new tab) |
| 9 | X-ERPC-Enforce-Non-Null-Tagged-Blocks | enforce-non-null-tagged-blocks | bool | enforceNonNullTaggedBlocks | true (SetDefaults) | Treats null block for tag-based eth_getBlockByNumber as an error. Disable for chains that legitimately return null for certain tags (e.g., zkSync). | architecture/evm/eth_getBlockByNumber.go:304 (opens in a new tab) |
| 10 | X-ERPC-Integrity | integrity | string | — | "" | Per-request data-integrity selection — a level (off/intrinsic/corroborated/authoritative) or a configured profile name; honored only when the network's integrity headerMode permits. See Integrity checks. | architecture/evm/hooks.go (opens in a new tab) |
server.executionHeaders — response diagnostic header mode
| Value | Effect | Source |
|---|---|---|
"all" (default) | Full counter headers + X-ERPC-Upstreams per-attempt trace | erpc/http_server.go:1081-1086 (opens in a new tab) |
"summary" | All counter headers; X-ERPC-Upstreams trace suppressed only | erpc/http_server.go:1105-1127 (opens in a new tab) |
"off" | No X-ERPC-* diagnostics (version/commit still emitted) | erpc/http_server.go:1105-1127 (opens in a new tab) |
Worked examples
All patterns below are distilled from real production fleets; comments explain the non-obvious choices.
1. Global network defaults: retryEmpty on, retryPending explicitly off. Production fleets set retryEmpty: true at networkDefaults so every network retries null/empty upstream responses (e.g., a not-yet-indexed block) without per-network config. retryPending: false is explicit because the struct's stale "true by default" comment causes confusion — production configs state it clearly:
networkDefaults: directiveDefaults: # Retry null/empty results globally — covers block-availability races # on eth_getLogs, eth_getBlockByNumber, eth_getTransactionReceipt. retryEmpty: true # retryPending default is false despite stale struct comment; # pin it explicitly so intent is clear to future maintainers. retryPending: false2. Non-standard chain: relax tagged-block enforcement. zkSync Era and similar ZK-rollups can legitimately return null for certain block tags, so they disable enforceNonNullTaggedBlocks per-network while inheriting all other defaults:
networks: - evm: chainId: 324 # zkSync Era mainnet directiveDefaults: # zkSync can return null for certain block tags — don't treat as error enforceNonNullTaggedBlocks: false3. Indexer workload — full receipt integrity. When backfilling a chain for an indexer you want to catch any upstream that sends partial or misordered receipts. Configure the network's integrity block so every call on that network is validated automatically, and keep the kept enforcement directives on at directiveDefaults:
directiveDefaults: retryEmpty: true # tx-receipt polling — only enable per-indexer-network, not globally retryPending: true enforceHighestBlock: true enforceNonNullTaggedBlocks: true4. Per-request upstream pinning. A dApp wants to read its own just-submitted transaction from the same upstream it used to broadcast, bypassing the normal load-balanced pool. Send the header at call time — no config change needed:
POST /1/main HTTP/1.1
X-ERPC-Use-Upstream: alchemy-mainnet
X-ERPC-Retry-Pending: trueThe upstream selector supports wildcards and tags: "alchemy-*", "alchemy-mainnet|quicknode-*", "family:archival" (tag match), "!drpc" (exclude one by ID).
5. Per-call cache bypass for fresh data. A price-feed service needs uncached eth_call results on every tick. Send X-ERPC-Skip-Cache-Read: true per request; write-back still fires so the next caller gets the fresh value from cache:
POST /1/main HTTP/1.1
X-ERPC-Skip-Cache-Read: trueTo bypass only the in-memory tier and keep Redis warm reads: X-ERPC-Skip-Cache-Read: memory*.
6. Skipping consensus for internal tooling. A monitoring script wants low-latency reads and trusts a single upstream. Bypass the consensus quorum for that call:
POST /1/main HTTP/1.1
X-ERPC-Skip-Consensus: trueRetry, hedge, circuit-breaker, and timeout still apply — only the dispute/agreement step is skipped.
Request/response behavior
Request headers — canonical truthy value. Every boolean directive header requires "true" (any case, optional surrounding whitespace). "1" and "yes" evaluate to false and silently have no effect. Exception: X-ERPC-Force-Trace (tracing subsystem, not a directive) accepts "true", "1", or "yes". Always use "true".
Directive-adjacent request inputs (not in the directive registry):
| Input | Kind | Values / behavior | Source |
|---|---|---|---|
X-ERPC-Force-Trace | header | "true", "1", or "yes" → bypass OTel trace sampling for that request (attribute erpc.force_trace = true) | common/tracing_util.go:107-115 (opens in a new tab) |
force-trace | query param | same three truthy values | common/tracing_core.go:31 (opens in a new tab) |
user-agent | query param | overrides User-Agent header for agent-name metrics tracking | common/request.go:1299-1314 (opens in a new tab) |
User-Agent | header | stored raw or simplified per project.userAgentMode; drives agent-name metric labels | common/request.go:1300-1311 (opens in a new tab) |
networkId | JSON body field | "evm:42161"-style network selection when architecture/chain are absent from the URL path | erpc/http_server.go:613-634 (opens in a new tab) |
Response headers always emitted (every HTTP response, not controlled by executionHeaders):
| Header | Value | Source |
|---|---|---|
Content-Type | application/json | erpc/http_server.go:259 (opens in a new tab) |
custom server.responseHeaders | static values, env-expanded at startup | erpc/http_server.go:263-266 (opens in a new tab) |
Execution diagnostic headers (controlled by server.executionHeaders; single responses get the per-response set, batch responses get ONE aggregated set — counters summed across sub-calls, X-ERPC-Duration = slowest sub-call, no X-ERPC-Upstream):
| Header | Value | When |
|---|---|---|
X-ERPC-Version | eRPC version string | always |
X-ERPC-Commit | git commit SHA | always |
X-ERPC-Attempts | total physical ops (upstream + cache) | always |
X-ERPC-Upstream-Attempts / X-ERPC-Upstream-Retries / X-ERPC-Upstream-Hedges | counters | always |
X-ERPC-Network-Attempts / X-ERPC-Network-Retries / X-ERPC-Network-Hedges | counters | always |
X-ERPC-Cache-Attempts / X-ERPC-Cache-Retries / X-ERPC-Cache-Hedges | counters | only when > 0 |
X-ERPC-Consensus-Slots / X-ERPC-Consensus-Disputes / X-ERPC-Consensus-Low-Participants | counters | only when > 0 |
X-ERPC-Cache | HIT or MISS; on batch responses also PARTIAL:<n> (n sub-calls served from cache) | when response metadata present |
X-ERPC-Upstream | winning upstream id | single responses only, when known |
X-ERPC-Duration | milliseconds (batch: the slowest sub-call) | NormalizedResponse only |
X-ERPC-Upstreams | per-attempt trace: upstreamId=reason:outcome:durationMs[:won] segments joined by ;. Known reason values: primary, hedge, retry, consensus_slot, sweep. Known outcome values: success, timeout, exec_revert, rate_limited. :won suffix appended only when the attempt's response contributed to the final answer (in a batch: to its own sub-call's answer). Batch: segments concatenate in sub-call order, capped at 50 with X-ERPC-Upstreams-Truncated: <dropped>. | all mode only; when len(attempts) > 0 |
Example X-ERPC-Upstreams value: alchemy=primary:success:50ms:won;quicknode=hedge:timeout:5000ms;drpc=consensus_slot:exec_revert:20ms.
reason precedence when several apply to one attempt: consensus_slot > hedge > sweep > retry > primary — the outermost fan-out cause wins, so an attempt that hedged or retried inside a consensus participant slot still reads consensus_slot (the attempt record's IsHedge/IsRetry fields preserve the inner mechanics).
Cost / billing headers (opt-in via server.costHeaders: true; emitted on single AND batch responses; omitted on early errors that never routed a call):
| Header | Value |
|---|---|
X-ERPC-Calls | routed JSON-RPC sub-calls in this HTTP response (batch size; 1 for a single request) |
X-ERPC-Billable | how many of them were billable. Successful, empty-but-valid and execution-revert responses bill (cache hits included — their cost is zero, not their billability; a revert is real work the node performed); protocol, transport, rate-limit and cancellation failures do not |
X-ERPC-Methods | distinct JSON-RPC methods, sorted, comma-joined |
X-ERPC-Credits | vendor:method=<units> segments, sorted, ;-joined — the vendor credit units consumed by every physical upstream attempt (retries, hedges and consensus slots included; skipped / breaker-open attempts provably never dialed and cost 0). Read from the per-request aggregate (NormalizedRequest.CreditUnitsByVendor). Omitted when nothing accrued (e.g. pure cache hits). Units come from vendors implementing CreditUnitsProvider (built-in tables: Alchemy CUs, QuickNode credits, dRPC CUs), merged per method with providers[].settings.creditUnits overrides; vendors with no table default to a flat 1 credit per request (opt out with creditUnits: {"*": 0}) |
X-ERPC-Credits-Total | grand total credit units across all vendors and sub-calls in this response; present alongside X-ERPC-Credits |
X-ERPC-Credits-Version | the eRPC version the built-in vendor tables shipped with; only present alongside X-ERPC-Credits |
Example: X-ERPC-Calls: 5 · X-ERPC-Billable: 4 · X-ERPC-Cache: PARTIAL:2 · X-ERPC-Credits: alchemy:eth_call=52;quicknode:eth_getLogs=40 · X-ERPC-Credits-Total: 92.
Cost-header gotchas: emitted only for routed requests — early parse/auth errors get none (there is no routed call to account for). Pricing is the vendor's logic (common.CreditUnitsProvider.CreditUnits(req, upstreamCfg); nothing is hard-coded in the eRPC layer). A timed-out attempt still accrues credits — it dialed the vendor — while skipped/breaker_open attempts provably never left the process and cost 0. Vendors without pricing cost a flat 1 credit per request unless opted out (creditUnits: {"*": 0}). Source: writeCostHeaders in erpc/http_server.go (opens in a new tab), attemptCreditUnits in upstream/upstream.go (opens in a new tab), tests in erpc/http_server_cost_test.go (opens in a new tab) and upstream/credit_units_test.go (opens in a new tab).
Error shapes produced by directive checks. Failed validation directives return ErrEndpointContentValidation. A UseUpstream selector that matches no upstream returns ErrUpstreamsExhausted (wrapping ErrUpstreamNotAllowed per candidate) — there is no distinct "selector matched nothing" error code.
Best practices
- Start with
enforceHighestBlock: true— it is on by default and catches the most common upstream data-quality issues with negligible overhead. - Raise the network's
integritylevel only for indexing workloads — higher levels (e.g. bloom recomputation, log-field checks) are CPU-intensive and penalize latency for general-purpose proxy traffic. UseX-ERPC-Integrityto opt a single request into a higher level. - Use
retryEmpty: truefor block-polling calls but verifyEmptyResultMaxAttemptsis set appropriately; unbounded retries on a degraded upstream can exhaust the timeout budget. - Never set
retryPending: trueglobally — it converts every pending-tx lookup into a polling loop. Pin it per request (X-ERPC-Retry-Pending: true) or to a dedicated network for transaction-tracking flows. - Pin
useUpstreamat config viadirectiveDefaultsfor known-good archival nodes rather than relying on callers to send the header — this prevents a misconfigured client from silently routing archival calls to full nodes. - Always send
"true", never"1"or"yes", forX-ERPC-*boolean directive headers. - On gRPC, per-request overrides are not available. Wire all desired defaults into
directiveDefaultsin config;EnrichFromHttpis never called on the gRPC path. - Lock down client directives on public-facing projects with
allowClientDirectives: "!skip-cache-read & !use-upstream"to prevent clients from bypassing your cache or pinning to specific upstreams while still allowing integrity directives.
Edge cases & gotchas
RetryPendingdefault isfalse, nottrue. The struct comment atcommon/request.go:125("true by default") is stale. NoSetDefaultsentry exists. Must be explicitly enabled viadirectiveDefaults.retryPending: trueorX-ERPC-Retry-Pending: true.X-ERPC-Use-Upstreamheader is NOT trimmed; query IS. A header with leading/trailing spaces will not match any upstream. Source:common/request.go:741(opens in a new tab) vs:812(opens in a new tab)."1"and"yes"are NOT truthy for directive headers. Only"true"(any case). This differs fromX-ERPC-Force-Trace. Source:common/request_test.go:406-427(opens in a new tab).UseUpstreamfailure producesErrUpstreamsExhausted, not a selector-specific error. Operators must parse the error message text to diagnose selector mismatches. Source:common/errors.go:1432-1440(opens in a new tab).X-ERPC-Skip-Consensus: falseactively disables consensus bypass. Sending the header with the value"false"is NOT the same as omitting the header. A header value"false"parses tofalseand overrides adirectiveDefaults.skipConsensus: trueconfig entry — the consensus branch then runs normally. Omitting the header leavesSkipConsensusat its config-default value. Tested:common/request_test.go:409(opens in a new tab),erpc/skip_consensus_directive_test.go:125-153(opens in a new tab).EnforceGetLogsBlockRangecannot be overridden per-request via HTTP.SetDefaultscopiesDirectiveDefaultsvalues intoEvm.Integrity; the architecture layer reads from the network config struct, not the per-request directive. Source:common/defaults.go:1957-1964(opens in a new tab).- Batch responses emit ONE aggregated
X-ERPC-*header set. Counters sum across sub-calls,X-ERPC-CachebecomesPARTIAL:<n>when mixed,X-ERPC-Durationis the slowest sub-call, the trace caps at 50 segments (X-ERPC-Upstreams-Truncatedcarries the overflow count), and the single-winnerX-ERPC-Upstreamis not emitted. Per-sub-call attribution still needs the per-responseidfields in the body. executionHeaders: summaryremoves onlyX-ERPC-Upstreams. All counter and metadata headers still emit. Use"off"to suppress everything.- Selector-scoped served-tip partitions are capped. Beyond
maxServedTipPartitionsper network, no partition is created and the stateless fallback is used silently — no error. Source:erpc/networks.go:429(opens in a new tab). nil *boolinDirectiveDefaultsConfig≠false *bool. Nil means "not set — skip"; a*falsepointer means "explicitly disable". Only non-nil pointers are applied inApplyDirectiveDefaults. Source:common/request.go:570-580(opens in a new tab).allowClientDirectivesfilters HTTP-supplied directives only. Config-setdirectiveDefaultsalways apply regardless of the filter. The pattern is pre-compiled at project registration viaNewWildcardMatcherand evaluated against each directive's query-param key (e.g.skip-cache-read,use-upstream).nil= all allowed;""= none allowed;"!skip-cache-read & !use-upstream"= all except those two. Does not filterX-ERPC-Force-Trace(processed before project resolution). Source:isDirectiveAllowedmethod onNormalizedRequestincommon/request.go,NewWildcardMatcherincommon/matcher.go,AllowClientDirectivesincommon/config.go. See projects config.
Observability
Directives have no dedicated Prometheus metrics; their effects appear in existing counters.
| Metric | Type | When it fires |
|---|---|---|
erpc_upstream_request_retries_total{reason="empty_result"} | counter | Each retry triggered by RetryEmpty |
erpc_upstream_request_retries_total{reason="pending_tx"} | counter | Each retry triggered by RetryPending |
erpc_upstream_request_retries_total{reason="integrity_validation"} | counter | Each retry due to a failed integrity check |
erpc_network_consensus_rounds_total | counter | Consensus rounds; SkipConsensus=true prevents increment |
Trace / log. X-ERPC-Force-Trace: true (or force-trace query param) forces OTel sampler to record the span regardless of sampling rate (attribute erpc.force_trace = true). Source: common/tracing_util.go:89-100 (opens in a new tab). Request.Lock / Request.RLock detail spans are emitted inside directive mutations (common/request.go:924-934 (opens in a new tab)). At trace level, applied request directives is logged with Interface("directives", ...) after every EnrichFromHttp call. Source: erpc/http_server.go:659 (opens in a new tab).
Source code entry points
common/request.go:116-215(opens in a new tab) —RequestDirectivesstruct: all directive fields + library-only fieldscommon/request.go:563-676(opens in a new tab) —ApplyDirectiveDefaults: idempotency guard, nil-check per*boolfield, copy-from-configcommon/request.go:702-893(opens in a new tab) —EnrichFromHttp: fast-path scan, clone-on-write, all header + query parserscommon/config.go:2105-2175(opens in a new tab) —DirectiveDefaultsConfigstruct;skipCacheReadcustom YAML/JSON unmarshalcommon/defaults.go:1454-1471(opens in a new tab) —DirectiveDefaultsConfig.SetDefaults: threetruedefaults (enforceHighestBlock,enforceGetLogsBlockRange,enforceNonNullTaggedBlocks)erpc/http_server.go:1081-1272(opens in a new tab) —executionHeadersMode,setResponseHeaders,writeCounterHeaders,writeResponseMetadataHeaders,writeUpstreamTraceHeaderserpc/network_executor.go:179-188(opens in a new tab) —SkipConsensusbrancherpc/network_executor.go:374-462(opens in a new tab) —shouldRetryWithReason:RetryEmpty/RetryPendingchecks andEmptyResultMaxAttemptscaperpc/networks.go:374-441(opens in a new tab) — selector-scoped served-tip:requestSelector,servedTipPartitionFor, partition capupstream/upstream.go:1505-1548(opens in a new tab) —shouldSkip:UseUpstreamselector check viaUpstreamMatchesSelectorcommon/matcher.go:34-112(opens in a new tab) —WildcardMatch,MatchesSelector,UpstreamMatchesSelectorerpc/skip_consensus_directive_test.go(opens in a new tab) — end-to-endSkipConsensustestscommon/request_test.go:391-541(opens in a new tab) — unit tests for boolean parse rules, header/query precedence,ValidateTransactionsRootoverride
Related pages
- Auth — credential headers (
X-ERPC-Secret-Token,Authorization,X-Siwe-*) parsed separately before directives. - Rate limiters — caps total request volume; important when
retryEmpty/retryPendingloops could multiply upstream calls. - Consensus — the branch that
skipConsensusbypasses. - Selection policies — controls which upstreams are eligible before
useUpstreamfurther restricts the set. - Matcher syntax — the WildcardMatch grammar used by
useUpstreamandskipCacheReadpatterns. - Survive provider outages — a use case that combines
retryEmpty,enforceHighestBlock, anduseUpstream.