Service won’t start
CUSTODY_DB_PASSWORD is required
CUSTODY_DB_PASSWORD is required
The service refuses to start without database credentials.Fix: set
CUSTODY_DB_PASSWORD, or enable CUSTODY_DB_IAM_AUTH=true for AWS RDS IAM authentication.CUSTODY_NODE_RPC_URL is required
CUSTODY_NODE_RPC_URL is required
The Quantum Chain RPC URL is missing.Fix: set
CUSTODY_NODE_RPC_URL to a reachable HTTP endpoint (e.g. https://api.quantumapi.io/v1/rpc or http://localhost:8545).CUSTODY_SIGNER_GRPC_TARGET is required when signer type is grpc
CUSTODY_SIGNER_GRPC_TARGET is required when signer type is grpc
You configured
CUSTODY_SIGNER_TYPE=grpc but did not provide a target.Fix: set CUSTODY_SIGNER_GRPC_TARGET=host:port or switch to callback mode for development.CUSTODY_WEBHOOK_ENCRYPTION_KEY must be 64 hex characters
CUSTODY_WEBHOOK_ENCRYPTION_KEY must be 64 hex characters
The encryption key is the wrong length.Fix: generate exactly 32 bytes and hex-encode them:
openssl rand -hex 32.Transactions stuck in PENDING_SIGNATURE
The most common cause: your external signer never delivered a signature.- Inspect the transaction:
Check
signing_expires_at. If it has passed, the signing window expired. - Verify your signer is reachable. For gRPC mode, the API logs
signer.unreachableand triggers the circuit breaker. - Confirm the wallet’s registered public key matches the key your signer is using. A mismatch produces signature verification failures, transitioning the transaction to
FAILED. - If the signer recovered, cancel the stale transaction and resubmit:
Transactions stuck in PENDING_AML_SCREENING
The compliance provider has not returned a result.- For ShuftiPro, results are delivered async to your
CUSTODY_SHUFTIPRO_CALLBACK_URL. Ensure the URL is reachable from the public internet. - Inspect the screening:
- If the provider is down, the circuit breaker opens after repeated failures. Set
CUSTODY_COMPLIANCE_PROVIDER=noopto bypass screening temporarily (development only).
Webhook deliveries failing
My endpoint returns 410 and deliveries stop
My endpoint returns 410 and deliveries stop
HTTP
410 Gone is treated as a permanent removal signal. The endpoint will be marked DISABLED and no further deliveries are attempted.Fix: create a new endpoint with POST /v1/webhooks and update your subscriber.Signatures don't verify
Signatures don't verify
Common causes:
- Using the formatted body instead of the raw bytes.
- Forgetting the
timestamp + "."prefix in the HMAC input. - Using the wrong webhook secret (each endpoint has its own).
Deliveries land in dead-letter queue
Deliveries land in dead-letter queue
After 5 failed retries, the delivery moves to dead-letter status.Fix: inspect with
GET /v1/webhooks/{id}/deliveries and replay successful ones with POST /v1/webhooks/{id}/replay/{deliveryId}.Nonce conflicts
Symptoms: transactions transition toFAILED with nonce too low or replacement transaction underpriced.
- Qustody assigns nonces sequentially per wallet. If you also send transactions from the same address through another path, you can collide.
- Fix: route all outbound transfers through Qustody for a given wallet, or wait for outstanding transactions to confirm before issuing new ones.
Circuit breaker open
The signer or AML provider triggered the circuit breaker. New transactions queue while the breaker is half-open.- Check the
signer_circuit_breaker_statePrometheus metric. - Investigate the provider; the breaker self-heals after the cool-down window.
- For long outages, switch the affected component to a fallback (e.g. set a backup
CUSTODY_NODE_RPC_URLviaCUSTODY_NODE_BACKUP_RPC_URLS).
Database connection saturation
If you seepq: too many connections for role, raise the pool limits or scale Postgres:
CUSTODY_DB_READ_REPLICA_ENABLED=true.
Authentication failures
| HTTP | Symptom | Cause |
|---|---|---|
401 Unauthorized | Missing or wrong token | Bearer header missing, expired SSO access token |
403 Forbidden | Authenticated but disallowed | RBAC permission missing for the resource+action |
409 Conflict | Transaction already in another state | E.g. trying to sign a BROADCASTING transaction |
429 Too Many Requests | Rate limited | Lower request rate or raise CUSTODY_RATE_LIMIT_RPM |
POST /v1/auth/refresh.
Where to look
| Source | What it tells you |
|---|---|
GET /readyz | Whether DB and signer are healthy |
:9090/metrics | Per-state queue depth, signer latency, breaker state |
Audit log (GET /v1/audit) | Who did what, when |
| Tracing (OTLP) | Per-request waterfall when CUSTODY_OTEL_ENABLED=true |
| Server logs | Structured JSON; filter by request_id |
Webhook deliveries (GET /v1/webhooks/{id}/deliveries) | Per-delivery status and response codes |