Foundation / Getting Started

Common SDK setup failures and the fastest path to resolve them.

9 categories51 pagesUpdated Mar 24, 2026

# Troubleshooting Use this page when the SDK is installed but your protected route is still not behaving correctly. ## Missing API Key - Symptom: SDK calls fail before the request is sent or return auth errors. - Check that COSANTOIR_API_KEY is set and scoped to the module you are calling. - Runtime integrations should prefer API keys over bearer tokens. ## Missing Site Id - Symptom: bearer-auth calls fail or the request cannot be mapped to policy. - Set COSANTOIR_SITE_ID on the client or on the adapter options. ## Missing Client IP - Symptom: middleware throws a configuration error about IP resolution. - Forward cf-connecting-ip, x-forwarded-for, x-real-ip, or supply an explicit resolver. ## Unexpected 429 Responses - Check Retry-After and the X-RateLimit-* headers. - If the error payload includes quota_exceeded with exhaustedDimension=monthly_budget_cap, the workspace has either already hit its monthly runtime budget cap or the next exact billable request would cross it. - Check the dashboard billing card for the next frontend and backend request estimates before retrying production traffic. - Raise the budget cap or wait for the next billing period before expecting production API-key traffic to succeed again. - Check x-dev-billing-cache to see whether the runtime used a fresh, cached, stale, or bypassed billing access lookup. - Check x-dev-replay-status to confirm you are not debugging a replayed response. - Inspect the active route key and burst policy for the path that is blocking. - Confirm that local test traffic is not reusing the same IP bucket. ## Unexpected 403 Responses - Check whether WAF returned block or challenge. - Inspect the matched rule id, rule type, and reason in the response body. - Verify whether the request path or user agent is hitting a custom expression or managed rule. ## Unexpected 401 Responses - Check whether the caller sent x-api-key or Authorization: Bearer ... at all. - Invalid and expired bearer tokens are now tracked in the dashboard's runtime auth-failure section instead of the decision log. - Invalid API keys and missing credentials are tracked there too, even when the runtime never resolved a site. - If a valid API key now points to a deleted or unavailable site, the auth-failure view will show api_key_site_unavailable. - Narrow the runtime auth-failure stream with auth_type and failure_reason filters before exporting for support or incident handoff. - Export the current workspace or site scope from the dashboard, or call GET /v1/dev/runtime-auth-failures/export?format=csv|jsonl for operator handoff. - The dashboard also derives operational alerts for sudden auth-failure spikes, repeated missing credentials, and api_key_site_unavailable regressions. - You can now route those alerts to generic signed webhooks, Slack incoming webhooks, or PagerDuty Events v2 from the runtime auth-failure panel. - Generic webhooks expose a one-time signing secret and send x-cosantoir-signature; Slack incoming webhooks do not use that secret or expose rotate-secret controls. - PagerDuty Events v2 destinations store only a routing-key hint in the console and require the official /v2/enqueue endpoint. - The notification panel also keeps recent configuration history so you can confirm who created, edited, rotated, archived, or switched provider types on a destination. - Configuration history can be filtered by action and provider type, then exported as CSV or JSONL for incident review or change tracking. - Those configuration-history filters persist in the dashboard URL, and you can save them as workspace or site-scoped presets for repeat investigations. - Saved presets can now be edited in place and given an explicit display order, so the incident-review views you use most stay pinned to the top of the panel. - Workspace presets and site-only presets now reorder independently, so changing one site's incident-review order does not reshuffle the workspace defaults. - The dashboard URL also persists project_id, site_id, auth_type, and failure_reason, so a shared incident link can reopen the same selected site and auth-failure slice. - Internal dashboard navigation now preserves that query state, so switching tabs or opening settings keeps the same project, site, and auth-failure context. - Shared dashboard links can now also target requests_module and analytics_panel, so operators can reopen the same runtime control-room section or analytics cluster after a refresh. - When a shared link pins project_id or site_id, the header exposes a reset action that clears only that control-plane context and keeps the current view, focus, and filters intact. - Billing and app-creation flows can now be reopened from a shared dashboard link with modal=payment or modal=app, while the rest of the dashboard context stays intact. - When those modals are opened from inside the dashboard, they now create a browser-history entry so close actions can behave like a real back navigation instead of flattening the session. - Runtime API key issuance now adds api_key_secret=<key_id> to the dashboard URL for recovery, but the plaintext secret stays only in same-session browser storage so a copied link does not leak the secret itself. - If that recovery link is opened after the same-session storage is gone, the keys page now tells the operator to revoke and reissue instead of pretending the one-time secret is still retrievable. - Generic notification destinations now do the same through notification_signing_secret=<destination_id>, so secret rotation or webhook creation can survive a refresh without exposing the signing secret in a shared link. - If that notification recovery link outlives the same-session storage, the dashboard now tells the operator to rotate the signing secret or recreate the generic destination instead of implying the plaintext secret can still be fetched. - Stale policy-save conflicts now also persist a policy_conflict=<reference> state in the dashboard URL, while the stale draft itself stays only in same-session browser storage. - The rules view now opens a merge workbench that highlights schema drift plus changed, current-only, and stale-only modules before you restore the stale draft. - Each changed shared module now also shows field-level drift and a patch preview, so you can see exactly which keys changed, only exist on the current head, or only exist in the stale draft. - Use the apply-selected-fields action to merge only the stale paths you intend to keep. - That means a refresh can still reopen the conflict panel and restore the stale draft for manual merge, but a copied link does not expose the policy JSON itself. ## No Decisions In The Dashboard - Confirm that the SDK is pointing at the correct COSANTOIR_GATEWAY_URL. - Confirm that COSANTOIR_SITE_ID matches the site selected in the dashboard. - Verify that the route is actually calling the SDK middleware or handler path. - Billing-access and monthly-budget preflight denials are logged once the runtime can resolve the site, so those failures should still appear in decisions and analytics. - For invalid credentials before site resolution, check the runtime auth-failure analytics panel instead of the decision log. - If you are load testing with idempotency enabled, confirm the request key is not replaying the same decision intentionally. ## Runtime Policy Or Replay Headers - x-dev-policy-cache=hit means the runtime used an in-process cached policy head. - x-dev-policy-cache=stale means the runtime served the most recent cached policy after a refresh failure. - x-dev-billing-cache=bypass means the request did not require a production API-key billing lookup. - x-dev-billing-cache=hit means the runtime reused a cached workspace billing-access snapshot. - x-dev-billing-cache=stale means the runtime served the most recent cached billing-access snapshot after a refresh failure. - x-dev-replay-status=bypass means no idempotency key was supplied for the request. - x-dev-replay-status=replayed means the runtime returned the original response and skipped duplicate writes. - 409 idempotency_conflict means the same idempotency key was reused with a different payload. - Once the runtime has already resolved the site and active policy head, idempotency_conflict is also written into the developer decision log as a non-billable block event. - 503 policy_unavailable means the runtime could not load an active policy and had no stale cache entry to fall back to. - policy_unavailable is also written into the decision log when site context exists, but its policy version fields are null because the runtime could not honestly resolve an active policy head. ## Next.js Quickstart Returns 503 - The apps/developer Next.js quickstart route requires DEVELOPER_RUNTIME_API_KEY and DEVELOPER_RUNTIME_SITE_ID. - Without those values the proxy fails open and the quickstart route reports missing configuration.

Last updated Mar 24, 2026