Operation
Directives
AI agents: fetch https://docs.erpc.cloud/operation/directives.llms.txt for the complete machine-readable version of this page (full configuration schema, defaults, worked examples, and source links). Append `.llms.txt` to any docs URL for the same treatment.AIFor agents: /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:

terminal
# 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[].networks[].directiveDefaults
erpc.yaml
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: true

Agent 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 headerQuery paramTypeConfig fieldDefaultEffectConsumed at
1X-ERPC-Retry-Emptyretry-emptyboolretryEmptyfalseRetry on null/empty upstream response. Subject to EmptyResultMaxAttempts cap.erpc/network_executor.go:427-442 (opens in a new tab)
2X-ERPC-Retry-Pendingretry-pendingboolretryPendingfalse (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)
3X-ERPC-Skip-Cache-Readskip-cache-readstring (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)
4X-ERPC-Use-Upstreamuse-upstreamstringuseUpstream"" (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)
5X-ERPC-Skip-Interpolationskip-interpolationboolskipInterpolationfalseSuppresses block-tag → hex substitution in forwarded params. Internal block refs still computed/cached.architecture/evm/json_rpc.go:132 (opens in a new tab)
6X-ERPC-Skip-Consensusskip-consensusboolskipConsensusfalseBypasses consensus branch; uses standard retry(hedge(upstreamSweep)). Retry/hedge/breaker/timeout still apply.erpc/network_executor.go:179-188 (opens in a new tab)
7X-ERPC-Enforce-Highest-Blockenforce-highest-blockboolenforceHighestBlocktrue (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)
8X-ERPC-Enforce-GetLogs-Rangeenforce-getlogs-rangeboolenforceGetLogsBlockRangetrue (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)
9X-ERPC-Enforce-Non-Null-Tagged-Blocksenforce-non-null-tagged-blocksboolenforceNonNullTaggedBlockstrue (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)
10X-ERPC-Integrityintegritystring""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

ValueEffectSource
"all" (default)Full counter headers + X-ERPC-Upstreams per-attempt traceerpc/http_server.go:1081-1086 (opens in a new tab)
"summary"All counter headers; X-ERPC-Upstreams trace suppressed onlyerpc/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:

projects[].networkDefaults.directiveDefaults
erpc.yaml
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: false

2. 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:

projects[].networks[].directiveDefaults
erpc.yaml
networks:  - evm:      chainId: 324  # zkSync Era mainnet    directiveDefaults:      # zkSync can return null for certain block tags — don't treat as error      enforceNonNullTaggedBlocks: false

3. 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:

projects[].networks[].directiveDefaults
erpc.yaml
directiveDefaults:  retryEmpty: true  # tx-receipt polling — only enable per-indexer-network, not globally  retryPending: true  enforceHighestBlock: true  enforceNonNullTaggedBlocks: true

4. 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: true

The 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: true

To 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: true

Retry, 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):

InputKindValues / behaviorSource
X-ERPC-Force-Traceheader"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-tracequery paramsame three truthy valuescommon/tracing_core.go:31 (opens in a new tab)
user-agentquery paramoverrides User-Agent header for agent-name metrics trackingcommon/request.go:1299-1314 (opens in a new tab)
User-Agentheaderstored raw or simplified per project.userAgentMode; drives agent-name metric labelscommon/request.go:1300-1311 (opens in a new tab)
networkIdJSON body field"evm:42161"-style network selection when architecture/chain are absent from the URL patherpc/http_server.go:613-634 (opens in a new tab)

Response headers always emitted (every HTTP response, not controlled by executionHeaders):

HeaderValueSource
Content-Typeapplication/jsonerpc/http_server.go:259 (opens in a new tab)
custom server.responseHeadersstatic values, env-expanded at startuperpc/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):

HeaderValueWhen
X-ERPC-VersioneRPC version stringalways
X-ERPC-Commitgit commit SHAalways
X-ERPC-Attemptstotal physical ops (upstream + cache)always
X-ERPC-Upstream-Attempts / X-ERPC-Upstream-Retries / X-ERPC-Upstream-Hedgescountersalways
X-ERPC-Network-Attempts / X-ERPC-Network-Retries / X-ERPC-Network-Hedgescountersalways
X-ERPC-Cache-Attempts / X-ERPC-Cache-Retries / X-ERPC-Cache-Hedgescountersonly when > 0
X-ERPC-Consensus-Slots / X-ERPC-Consensus-Disputes / X-ERPC-Consensus-Low-Participantscountersonly when > 0
X-ERPC-CacheHIT or MISS; on batch responses also PARTIAL:<n> (n sub-calls served from cache)when response metadata present
X-ERPC-Upstreamwinning upstream idsingle responses only, when known
X-ERPC-Durationmilliseconds (batch: the slowest sub-call)NormalizedResponse only
X-ERPC-Upstreamsper-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):

HeaderValue
X-ERPC-Callsrouted JSON-RPC sub-calls in this HTTP response (batch size; 1 for a single request)
X-ERPC-Billablehow 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-Methodsdistinct JSON-RPC methods, sorted, comma-joined
X-ERPC-Creditsvendor: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-Totalgrand total credit units across all vendors and sub-calls in this response; present alongside X-ERPC-Credits
X-ERPC-Credits-Versionthe 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 integrity level only for indexing workloads — higher levels (e.g. bloom recomputation, log-field checks) are CPU-intensive and penalize latency for general-purpose proxy traffic. Use X-ERPC-Integrity to opt a single request into a higher level.
  • Use retryEmpty: true for block-polling calls but verify EmptyResultMaxAttempts is set appropriately; unbounded retries on a degraded upstream can exhaust the timeout budget.
  • Never set retryPending: true globally — 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 useUpstream at config via directiveDefaults for 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", for X-ERPC-* boolean directive headers.
  • On gRPC, per-request overrides are not available. Wire all desired defaults into directiveDefaults in config; EnrichFromHttp is 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

  1. RetryPending default is false, not true. The struct comment at common/request.go:125 ("true by default") is stale. No SetDefaults entry exists. Must be explicitly enabled via directiveDefaults.retryPending: true or X-ERPC-Retry-Pending: true.
  2. X-ERPC-Use-Upstream header 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).
  3. "1" and "yes" are NOT truthy for directive headers. Only "true" (any case). This differs from X-ERPC-Force-Trace. Source: common/request_test.go:406-427 (opens in a new tab).
  4. UseUpstream failure produces ErrUpstreamsExhausted, 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).
  5. X-ERPC-Skip-Consensus: false actively disables consensus bypass. Sending the header with the value "false" is NOT the same as omitting the header. A header value "false" parses to false and overrides a directiveDefaults.skipConsensus: true config entry — the consensus branch then runs normally. Omitting the header leaves SkipConsensus at 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).
  6. EnforceGetLogsBlockRange cannot be overridden per-request via HTTP. SetDefaults copies DirectiveDefaults values into Evm.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).
  7. Batch responses emit ONE aggregated X-ERPC-* header set. Counters sum across sub-calls, X-ERPC-Cache becomes PARTIAL:<n> when mixed, X-ERPC-Duration is the slowest sub-call, the trace caps at 50 segments (X-ERPC-Upstreams-Truncated carries the overflow count), and the single-winner X-ERPC-Upstream is not emitted. Per-sub-call attribution still needs the per-response id fields in the body.
  8. executionHeaders: summary removes only X-ERPC-Upstreams. All counter and metadata headers still emit. Use "off" to suppress everything.
  9. Selector-scoped served-tip partitions are capped. Beyond maxServedTipPartitions per network, no partition is created and the stateless fallback is used silently — no error. Source: erpc/networks.go:429 (opens in a new tab).
  10. nil *bool in DirectiveDefaultsConfigfalse *bool. Nil means "not set — skip"; a *false pointer means "explicitly disable". Only non-nil pointers are applied in ApplyDirectiveDefaults. Source: common/request.go:570-580 (opens in a new tab).
  11. allowClientDirectives filters HTTP-supplied directives only. Config-set directiveDefaults always apply regardless of the filter. The pattern is pre-compiled at project registration via NewWildcardMatcher and 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 filter X-ERPC-Force-Trace (processed before project resolution). Source: isDirectiveAllowed method on NormalizedRequest in common/request.go, NewWildcardMatcher in common/matcher.go, AllowClientDirectives in common/config.go. See projects config.

Observability

Directives have no dedicated Prometheus metrics; their effects appear in existing counters.

MetricTypeWhen it fires
erpc_upstream_request_retries_total{reason="empty_result"}counterEach retry triggered by RetryEmpty
erpc_upstream_request_retries_total{reason="pending_tx"}counterEach retry triggered by RetryPending
erpc_upstream_request_retries_total{reason="integrity_validation"}counterEach retry due to a failed integrity check
erpc_network_consensus_rounds_totalcounterConsensus 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

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/retryPending loops could multiply upstream calls.
  • Consensus — the branch that skipConsensus bypasses.
  • Selection policies — controls which upstreams are eligible before useUpstream further restricts the set.
  • Matcher syntax — the WildcardMatch grammar used by useUpstream and skipCacheRead patterns.
  • Survive provider outages — a use case that combines retryEmpty, enforceHighestBlock, and useUpstream.