- Initial sync failures - surfaced as a non-nil error from
NewClient. Refuse to start the application; an empty cache is unsafe to serve. - Background sync / SSE failures - never returned to the caller. They are logged via
Logger.Errorf(...), and the existing cache continues to be served.
Sentinel errors
The SDK exports the following sentinel error variables. Always check witherrors.Is(err, ...) rather than equality or string matching:
errors.Is and errors.As, and they’re the way io.EOF, os.ErrNotExist, sql.ErrNoRows, etc. work in the standard library.
When each is returned
What is NOT returned as an error
- Background syncs - silently retried on the next TTL tick. Cache served.
- SSE transport errors - exponential backoff, 3-strike fallback to polling.
RunStreamreturns normally. 304 Not Modified- treated as success; TTL reset.- Missing config / flag -
GetConfigreturns(nil, false);GetFlagreturns thedefaultValue.
The asymmetry between secrets (return error) and configs/flags (return
ok/default) is deliberate: a missing secret usually means “you’re misconfigured, fail fast”, while a missing flag usually means “use the safe default and continue”.402 Payment Required - ErrBilling
When the backend returns HTTP 402 (the tenant has at least one invoice overdue by more than 14 days), the SDK:
- Marks the client’s billing-overdue flag (
client.BillingOverdue() == true). - Halts the background refresh loop - read methods continue to serve the last cached snapshot.
- Returns
ErrBillingfromSync(...).
Sync succeeds.
WIF-specific errors
WhenWIFConfig.Enabled is true:
ErrWIFNotConfigured- the configured (or auto-detected) provider isn’t usable in the current environment. Either the K8s service-account token file is missing, theAWS_WEB_IDENTITY_TOKEN_FILEenv var isn’t set, or the GCP/Azure metadata endpoint isn’t reachable. Pass an explicitTokenSourceto bypass auto-detection.ErrWIFTokenExchangeFailed- the backend rejected the OIDC token, or the response could not be decoded. Inspect the wrapped error for the HTTP status. Most often caused by an audience mismatch or a clock skew between the provider and the Nexus backend.ErrSessionExpired- only fires when a refresh attempt fails AND no staticAPIKeyfallback is configured. Provide bothWIFandAPIKeyif you need automatic fallback in degraded conditions.
ErrServiceKindMismatch (frontend services)
GetSecret(...) on a service whose kind is "frontend" returns ErrServiceKindMismatch. Check client.Kind() to know the kind:
Failure scenarios
Wrong API key
ErrSyncFailed is the outer error; the cause is wrapped - errors.Is(err, nexus.ErrUnauthorized) matches transitively.
Slug doesn’t match key (Double-Gate failure)
In production, the backend validates that the slug embedded inBaseURL matches the API key’s service. A mismatch returns 401 - same surface as above.
Network down at startup
If the Nexus backend is unreachable whenNewClient runs, the function returns an error wrapping ErrSyncFailed whose cause is a *url.Error. The decision is yours: crash, or wrap with retry/backoff.
Reading a secret with a public key
Reading a secret that doesn’t exist
SSE-specific behaviour
RunStream failures never propagate to your code as errors. The SDK logs them at Errorf level and returns when:
- The context is cancelled (clean shutdown)
- 3 consecutive transport errors exhaust the backoff (silent fallback)
RunStream with a done channel and check whether it returned before ctx.Done():
