Troubleshooting
Find answers to common questions and solutions to frequently encountered issues. If you can't find what you're looking for, contact support.
Jump to:
Common Error Codes
| Code | Body code | Meaning | Action |
|---|---|---|---|
400 | BAD_REQUEST / VALIDATION_ERROR | Bad Request | Check request body format and required fields |
401 | UNAUTHORIZED / AUTHENTICATION_FAILED | Unauthorized | Send the key in the Authorization: Bearer header (not the query string) and verify it is valid |
403 | ACCESS_DENIED | Forbidden | Check permissions and resource ownership |
404 | NOT_FOUND | Not Found | Verify resource ID and endpoint URL |
409 | CONFLICT / LEASE_EXPIRED | Conflict / lease expired | Resource already exists (idempotency key); or stop work — job lease timed out |
413 | PAYLOAD_TOO_LARGE | Payload too large | Shrink payload or upgrade plan; see limits |
422 | VALIDATION_ERROR | Unprocessable Entity | Valid JSON but invalid data (check field values / missing fields) |
429 | RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED | Rate limited / quota exceeded | Back off and retry; if QUOTA_EXCEEDED, upgrade your plan |
500 | INTERNAL_ERROR / DATABASE_ERROR / CACHE_ERROR | Server Error | Retry with backoff, contact support if persistent |
503 | — | Service Unavailable | Check status page, retry later |
Jobs Not Processing
Jobs stay in "pending" status
Symptom / confirm
Jobs are created successfully but never move to "processing" state.
- No workers running: Ensure you have at least one worker connected to the queue
- Workers on different queue: Verify workers are listening to the correct queue name
- Workers paused: Check if the queue or workers are paused in the dashboard
- Scheduled for future: Jobs with
scheduled_atwon't process until that time
Fix
How to diagnose:
# Check if workers are registered
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://api.spooled.cloud/api/v1/workers
# Check job details
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://api.spooled.cloud/api/v1/jobs/JOB_IDJobs stuck in "processing" state
Symptom / confirm
Jobs move to processing but never complete.
- Worker crashed: Worker died without completing the job
- Long-running job: Job is taking longer than expected
- Heartbeat timeout: Worker didn't send heartbeats (lease expired)
Fix
Jobs with expired leases are automatically returned to pending. If this happens frequently:
- Increase heartbeat frequency in your worker
- Implement proper heartbeating for long-running jobs
- Check worker logs for crashes
Authentication Errors
401 Unauthorized
Symptom / confirm
API requests return 401 Unauthorized.
- Missing API key: Ensure
Authorization: Bearer YOUR_API_KEYheader is present - Credential in the query string: REST rejects
?api_key=and?token=— a previously working integration that passed either one now gets a bare 401 - Invalid API key: Key may be revoked or incorrectly copied
- Wrong environment: Using test key with production endpoint or vice versa
Fix
Move the credential out of the URL and into the Authorization: Bearer …
header. Only /api/v1/ws, /api/v1/events,
/api/v1/events/jobs/{id} and
/api/v1/events/queues/{name} still take it in the query
string — browser EventSource and
WebSocket clients cannot set headers. Quick check:
# Verify your API key works
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://api.spooled.cloud/api/v1/health403 Forbidden
Symptom / confirm
API requests return 403 Forbidden.
- Organization mismatch: Accessing resources from a different organization
- Insufficient permissions: API key doesn't have required scope
- Rate limited: Too many failed authentication attempts
Fix
- Use an API key from the same organization as the resource
- Use a key with the required scope
- Stop hammering auth after failures (failed attempts can rate-limit)
JWT Token Expired
Symptom / confirm
Dashboard sessions expire frequently, workers can't authenticate.
Fix
- For dashboard: Clear cookies and log in again
- For workers: Ensure workers refresh tokens before expiration
- Check server clock synchronization (NTP)
Webhook Issues
Webhooks not being received
Symptom / confirm
Jobs complete but webhook notifications never arrive.
- Endpoint auto-disabled: after 20 consecutive failed deliveries the webhook is switched off —
enabledisfalseandlast_statusisauto_disabled. Check this first: fixing the URL, cert or status code changes nothing while it is off - Verify webhook URL is correctly configured in organization settings
- Ensure your endpoint is publicly accessible (not localhost)
- Check that your endpoint returns
2xxstatus code - Verify SSL certificate is valid (we don't deliver to invalid certs in production)
Fix
- Re-enable the endpoint once it is healthy:
PUT /api/v1/outgoing-webhooks/{id}with{"enabled": true}. That counts against your plan webhook cap, so it can return429 QUOTA_EXCEEDED— see outgoing webhook delivery - Update the webhook URL in organization settings
- Expose the endpoint (tunnel / public host) and retest delivery
- Return 2xx quickly; fix cert chain if TLS fails in production
Webhook signature validation failing
Symptom / confirm
Receiving webhooks but signature verification fails.
- Wrong secret: Using incorrect webhook secret for verification
- Secret was cleared: an update that sent
"secret": nullremoves it, and deliveries then arrive with noX-Spooled-Signatureheader at all - Encoding issues: Not using raw request body for signature verification
- Timestamp tolerance: Clock drift causing timestamp validation to fail
Fix
Correct verification pattern:
// Node.js example
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Webhook delivery timeouts
Symptom / confirm
Webhooks marked as failed with timeout errors.
Fix
- Ensure your endpoint responds within 10 seconds
- Process webhooks asynchronously (respond 200 immediately, process later)
- Check your server isn't overloaded
Worker Problems
Workers disconnecting frequently
Symptom / confirm
Workers show as unhealthy, reconnecting constantly.
- Network instability: Unstable connection to Spooled Cloud
- Resource exhaustion: Worker running out of memory/CPU
- Firewall issues: gRPC connections being terminated
Fix
- Implement reconnection logic with exponential backoff
- Monitor worker resource usage
- Ensure firewalls allow long-lived HTTP/2 connections
Worker can't register (429 QUOTA_EXCEEDED)
Symptom / confirm
A restarting worker is refused with 429 and "resource": "workers", while the dashboard shows workers you no longer run.
- No stable
worker_id: each start mints a fresh UUID and abandons the previous row, which keeps holding a slot in the plan worker cap until the stale-worker reaper clears it (about two minutes) - Crash loop: a worker restarting faster than that leaks a row per restart and locks itself out
Fix
- Pass a stable
worker_idonPOST /api/v1/workers/register— hostname, pod name or container id. Registration becomes an upsert and re-registering your own id is not charged against the cap - Deregister on shutdown so the slot is released immediately
- Wait out the reaper (about two minutes) to clear rows already leaked, then retry
Workers not receiving jobs
Symptom / confirm
Worker is connected but never receives jobs.
- Verify worker is subscribed to the correct queue name(s)
- Check there are pending jobs in that queue
- Ensure worker concurrency is set correctly (not 0)
- Verify worker health status in dashboard
Fix
- Align worker queue subscription with the queue that has pending jobs
- Raise concurrency above 0
- Confirm health is green in the dashboard before retrying
Duplicate job processing
Symptom / confirm
Same job processed multiple times.
- Worker crashed during processing
- Network partition: Job lease expired
- Missing acknowledgment
Fix
- Implement idempotent job handlers
- Use longer lease durations
- Always complete or fail jobs explicitly
Rate Limiting
429 Too Many Requests
Symptom / confirm
API requests return 429 status code.
Two conditions return 429:
- Per-second rate limiting — too many requests in a short window (includes a
Retry-Afterheader). Bodycode:RATE_LIMIT_EXCEEDED. - Plan quota / limit exceeded — body carries
"code": "QUOTA_EXCEEDED"withresource,current,limit, andplan. See Rate Limits & Quotas.
Plan rate limits (req/sec · burst): Free 5 · 10, Starter 25 · 50, Pro 100 · 200, Enterprise 500 · 1000. Full table: plan comparison.
Fix
- Implement exponential backoff on 429 responses
- Use bulk operations (e.g.,
bulkEnqueue) instead of individual requests - Contact support for higher limits if needed
Handling rate limits:
// Exponential backoff example
async function apiCallWithRetry(fn, maxRetries = 5) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.status === 429 && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s, 8s, 16s
await new Promise((r) => setTimeout(r, delay));
continue;
}
throw error;
}
}
}Debugging Tips
Enable verbose logging
For SDK debugging, enable verbose mode:
// Node.js SDK
const client = new SpooledClient({
apiKey: 'YOUR_API_KEY',
debug: true,
});# Python SDK
import logging
logging.basicConfig(level=logging.DEBUG)
client = SpooledClient(api_key='YOUR_API_KEY')Use the Dashboard
The Spooled Dashboard provides:
- Real-time job status and history
- Worker health monitoring
- Queue metrics and throughput graphs
- DLQ management for failed jobs
- Webhook delivery logs
Still Stuck?
Need Help?
If you can't find the answer here, we're happy to help:
- Email: [email protected] (response within 24 business hours)
- GitHub: Open an issue for bugs or feature requests
- Community: GitHub Discussions for questions and community help
When contacting support, please include:
- Your organization ID (visible in Dashboard)
- Relevant job IDs or timestamps
- Error messages (full response if possible)
- Steps to reproduce the issue
- SDK version (if using an SDK)