Skip to main content

Troubleshooting

Common issues, what they mean, and how to fix them. If something isn’t covered here, ping us via the contact channels at the bottom of the page.

HMAC errors

Before debugging, run the handshake test. Click Test connection in your provider dashboard (POST /providers/id/:id/hmac/test), or npx shadowfeed verify --endpoint <url> --secret <secret> if you use the SDK. It signs and probes your endpoint exactly like a real buyer and tells you the precise stage that failed — usually faster than reading logs.

401 invalid HMAC signature on every request

Two causes, equally common: secret mismatch, or path mismatch. Check both. Secret mismatch — same secret on both sides?
1

Check the env var name on your server

From the dashboard → View setup guide → the placeholder env var line shows the name your server expects (SHADOWFEED_PARTNER_SECRET).
2

Verify it's set

On your server: echo $SHADOWFEED_PARTNER_SECRET (or your platform’s equivalent — Worker secret, Vercel env, etc.).
3

Rotate if unsure

If the secret might be wrong or lost, rotate from the dashboard. Update your server’s env var with the new value and redeploy.
Path mismatch — does the PATH you verify equal the PATH ShadowFeed signed? ShadowFeed signs the registered source_path (relative to partner_endpoint). If your verifier builds the canonical string from new URL(req.url).pathname and your app is mounted behind a prefix (common with x402 routers) or your partner_endpoint includes a path, the two PATHs differ and the signature can never match.
  • Confirm partner_endpoint is origin-only (https://api.you.com, no path, no trailing slash).
  • If your router is mounted under a prefix (/api, /x402, …), strip it before signing — see HMAC Integration → Mounted behind a prefix.
  • The SDK adapters handle this automatically.

ShadowFeed can’t reach my route — 404 or “route exists?”

Cause: ShadowFeed calls partner_endpoint + source_path literally. A 404 means that combined URL doesn’t resolve to a real route on your server — your signature code never even runs. This is a silent killer: you’re watching for 401s that never arrive while every call 404s. Fix:
  • Register partner_endpoint as origin-only and source_path as the exact path your server already serves (e.g. your existing x402 route).
  • A stale /v1 or duplicated segment is the usual culprit — partner_endpoint = https://api.you.com/v1 and source_path = /v1/whales produces …/v1/v1/whales.
  • Run Test connection — the response echoes probed_path and the upstream HTTP status so you can see exactly what ShadowFeed hit.

Buyers pay STX but I earn nothing (my logs show a 402 to ShadowFeed)

Cause: you already gate this endpoint with x402 (Solana/EVM), and the HMAC bypass didn’t fire — so the ShadowFeed call fell through to your paywall, which answered 402. ShadowFeed treats any non-2xx upstream response as a failed delivery: no revenue is credited and the agent gets an error, even though they paid STX. Fix:
  • Mount the HMAC verifier before your x402 middleware, and set your skipX402 flag on a valid signature.
  • The underlying reason the bypass failed is almost always a secret or path mismatch — fix that (sections above) and the 402s turn into 200s. See Coexisting with your existing x402 paywall.
If both sides have the same secret, the path matches, and it still fails, see the next section.

Some calls succeed, some return 401

Likely cause: clock drift > 5 minutes between your server and ShadowFeed.
# On your server
date -u
# Compare to https://time.is
Fix:
  • Linux: enable systemd-timesyncd or ntpd
  • Cloud platforms (Cloudflare Workers, Vercel, Render, Railway) sync time automatically — if you’re on one of these and seeing drift, something else is wrong

401 on POST requests but GET works

Cause: middleware reads the body to verify HMAC, then it’s already consumed when the route handler runs (or vice versa).
StackFix
TypeScript / Fetch APIUse req.clone().text() so original body remains readable
ExpressInstall body-parser, then access req.rawBody
FastAPIUse await request.body() once in middleware, store on request.state
GoRead body in middleware, then r.Body = io.NopCloser(strings.NewReader(...))

Worked yesterday, all 401 today

Likely cause: you rotated the secret in the dashboard but didn’t update your server’s env var. Fix: pull the latest secret (or rotate again if you didn’t save it) → redeploy your server with the new value.

Replay attack rejection: nonce already used

If your verifier returns this for legitimate requests, your nonce cache is too aggressive or there’s a retry happening.
The nonce should expire after 5 minutes. Each ShadowFeed request uses a fresh UUID v4. If you’re behind a proxy that retries on timeout, the proxy may be sending the SAME signed request twice — increase your upstream timeout to 30s+.

Onboarding errors

”handle already taken”

Pick a different handle. URLs are unique. Once chosen, the handle is permanent for that provider record.

”invalid or reserved handle”

Reserved handles (admin, api, auth, dashboard, system, etc.) are blocked.
  • Allowed characters: lowercase letters, numbers, hyphens
  • Length: 3-32 chars
  • Must start and end with alphanumeric

”partner_endpoint must be https://…”

We only proxy to HTTPS endpoints. Set up TLS on your API (Cloudflare proxy, Let’s Encrypt) or use a managed runtime that handles it (Workers, Vercel, Render).

Wizard step 4 didn’t show me the HMAC secret

There’s no HMAC secret in this mode — we host your data, no signed requests needed.

Withdrawal errors

”no linked withdrawal wallet”

You must link a Stacks wallet before withdrawing. See Withdrawals → Linking a withdrawal wallet.

”destination must match your linked withdrawal wallet”

Withdrawals can only go to the wallet you linked. To send elsewhere, re-link with the new address first (signature challenge).
This is a security guard — without it, a session hijack could drain funds to an attacker’s wallet.

”minimum withdrawal is 1 STX”

We have a 1 STX floor to avoid dust transactions. Accumulate more revenue first, or contact us if you have a strong use case for a smaller withdrawal.

”insufficient pending balance”

You’re trying to withdraw more than your current pending_revenue. Check the dashboard — pending balance updates ~immediately after each paid query but may have small lag during high traffic.

Withdrawal stuck in broadcast state

The cron poller checks pending withdrawals every minute. Most Stacks blocks land within 10 minutes. If after 30+ minutes the status hasn’t changed:
1

Click the TX hash

Opens Hiro Explorer.
2

Check status

If success → wait a few more minutes for our poller to catch up.
3

If failed

abort_by_response or abort_by_post_condition means the tx failed on-chain (rare; usually a platform wallet nonce conflict). Funds are auto-restored to pending revenue, withdrawal marked failed. Retry.

”broadcast failed — funds restored to pending balance”

Your withdrawal was rolled back. Check the error detail (returned in the API response). Common causes:
  • Platform wallet low on STX (we’ll catch this and resolve)
  • Hiro API rate limit (auto-retry will kick in)
  • Network glitch (try again)
Your pending_revenue is back where it was. Just retry.

Feed errors

My feed returns 503 “provider inactive”

Your provider is in pending or paused state.
  • pending: you haven’t added a feed yet. Add at least one feed to auto-activate.
  • paused: you (or platform admin) manually paused. Click Resume in the dashboard.

Feed returns 503 “feed paused”

You paused this individual feed. Click Resume in the feed list.

Feed returns 502 with upstream error

Your partner_endpoint returned a 5xx, or wasn’t reachable. Common causes:
  • Endpoint URL is wrong (typo, missing path, http vs https)
  • Your server is down
  • DNS resolution failed
  • TLS certificate expired
  • Firewall blocking incoming traffic from Cloudflare
In this case, the buyer still paid 0.005 STX, but we don’t credit your revenue counter (you didn’t actually serve data). The platform fee is also waived for these failures.
Test from your local machine:
curl -i https://your-api.com/your/feed/path
If your endpoint works locally but fails when called from ShadowFeed, check IP allowlists or geoblocking.

Hosted mirror feed shows “feed not yet populated”

The cron poller hasn’t fetched your data source yet. Wait up to one poll interval (default 5 minutes). If still empty after 2 intervals:
  • Check last_poll_status in your feed config (visible in dashboard)
  • Verify the source URL returns valid JSON: curl https://your-source-url
  • Check payload size — we cap at 1 MB per feed

Dashboard / SIWS errors

”Connect a Stacks wallet first”

Your wallet provider (Leather or Xverse) isn’t installed or not loaded yet.
1

Reload the page

Sometimes the extension takes a moment to inject.
2

Check the extension

Make sure Leather or Xverse is installed and unlocked.
3

Connect first, then sign in

Click Connect Wallet in the top right BEFORE clicking Sign in with Stacks.

”signature verification failed”

The signature doesn’t match expected, or the nonce is stale.
  • Try signing again — Leather sometimes loses focus mid-flow
  • If using Xverse, make sure you’re on the latest version
  • If persistent: sign out fully → reload → sign back in

Session expired

We auto-clear sessions after 7 days. Sign in again — your provider account and revenue are unchanged.

Reach out

If you’re stuck:
  • DM us on X — @shadow_agents
  • Open an issue on GitHub (link in footer)
  • Email — grants@stacksendowment.co (during M2 grant period)
When asking for help, include:
  1. Your provider handle
  2. Stack you’re integrating from (TS / Python / Go / etc.)
  3. Exact error message
  4. A request ID or timestamp if you have it (we can grep server logs)

What’s next

HMAC Integration Guide

Verifier code in TypeScript, Python, and Go.

Withdrawals & Revenue

How revenue accounting works.