Troubleshooting
Common issues and solutions for Opacus Protocol.
Connection Issues
❌ Cannot Connect to Gateway
Error Message
Error: Failed to connect to gateway at wss://gateway.opacus.ai
WebSocket connection failed: ECONNREFUSED
Possible Causes:
- Gateway is down or unreachable
- Network firewall blocking WebSocket connections
- Incorrect gateway URL
- TLS certificate issues
Solutions:
// 1. Check gateway status
curl https://gateway.opacus.ai/health
// Expected: { "status": "ok", "version": "1.0.0" }
// 2. Verify WebSocket connectivity
wscat -c wss://gateway.opacus.ai
// Should connect without errors
// 3. Try alternative protocol
const client = new OpacusClient({
gateway: 'gateway.opacus.ai',
protocol: 'websocket' // Force WebSocket instead of WebTransport
});
// 4. Check firewall rules
// Allow outbound connections on ports 443 (WSS) and 4433 (QUIC)
❌ Connection Drops Frequently
Solutions:
// TypeScript: Enable keepalive
const client = new OpacusClient({
gateway: 'gateway.opacus.ai',
keepalive: true,
keepaliveInterval: 30000, // 30 seconds
reconnect: true,
maxReconnectAttempts: 5
});
// Handle reconnection
client.on('reconnect', (attempt) => {
console.log(`Reconnecting (attempt ${attempt})...`);
});
client.on('reconnected', () => {
console.log('Connection restored');
});
// Rust: Configure keepalive
let config = OpacusConfig {
gateway_url: "gateway.opacus.ai".to_string(),
keep_alive_interval: Duration::from_secs(30),
max_idle_timeout: Duration::from_secs(120),
..Default::default()
};
let client = OpacusClient::new(config).await?;
Authentication & Identity
❌ Agent Registration Fails
Error Message
Error: Transaction reverted: Agent already exists
Solution:
// Check if agent already registered
const agentId = keccak256(abi.encodePacked(edPublicKey, xPublicKey));
const agent = await agentRegistry.getAgent(agentId);
if (agent.active) {
console.log('Agent already registered:', agentId);
// Use existing agent
} else {
// Register new agent
await agentRegistry.registerAgent(metadata, edPublicKey, xPublicKey);
}
❌ Signature Verification Failed
Error Message
Error: Invalid signature: Ed25519 verification failed
Possible Causes:
- Wrong private key used for signing
- Message data modified after signing
- Public key mismatch
- Incorrect signature encoding
Solution:
// TypeScript: Verify signature locally first
import { ed25519 } from '@noble/curves/ed25519';
const message = 'Hello, world!';
const messageBytes = new TextEncoder().encode(message);
// Sign
const signature = ed25519.sign(messageBytes, privateKey);
// Verify immediately
const valid = ed25519.verify(signature, messageBytes, publicKey);
console.log('Signature valid:', valid);
// Common mistake: signing different format
// ❌ Wrong:
const sig1 = ed25519.sign(message, privateKey); // String
// ✅ Correct:
const sig2 = ed25519.sign(messageBytes, privateKey); // Uint8Array
❌ Public Key Not Found On-Chain
Solution:
// Check agent registration status
const agentId = '0x...';
try {
const agent = await agentRegistry.getAgent(agentId);
if (!agent.active) {
console.error('Agent is deactivated');
// Reactivate if you own it
await agentRegistry.reactivateAgent(agentId);
}
console.log('Public keys:', {
ed25519: agent.edPublicKey,
x25519: agent.xPublicKey
});
} catch (err) {
console.error('Agent not found:', agentId);
// Register the agent first
}
Encryption & Decryption
❌ Decryption Failed
Error Message
Error: Decryption failed: authentication tag mismatch
Possible Causes:
- Wrong decryption key
- Corrupted ciphertext
- Incorrect nonce or IV
- Key derivation mismatch
Solution:
// TypeScript: Debug encryption/decryption
import { x25519 } from '@noble/curves/ed25519';
// Sender side
const alicePrivate = x25519.utils.randomPrivateKey();
const alicePublic = x25519.getPublicKey(alicePrivate);
const bobPublic = await getRecipientPublicKey(bobAgentId);
// Derive shared secret
const sharedSecret = x25519.scalarMult(alicePrivate, bobPublic);
console.log('Shared secret (sender):', Buffer.from(sharedSecret).toString('hex'));
// Recipient side
const bobPrivate = x25519.utils.randomPrivateKey();
const bobPublic2 = x25519.getPublicKey(bobPrivate);
// Derive same shared secret
const sharedSecret2 = x25519.scalarMult(bobPrivate, alicePublic);
console.log('Shared secret (recipient):', Buffer.from(sharedSecret2).toString('hex'));
// ✅ Both should match!
assert(Buffer.from(sharedSecret).equals(Buffer.from(sharedSecret2)));
❌ Key Derivation Mismatch
// Ensure consistent key derivation
import { hkdf } from '@noble/hashes/hkdf';
import { sha256 } from '@noble/hashes/sha256';
function deriveMessageKey(
sharedSecret: Uint8Array,
messageId: string,
keyLength: number = 32
): Uint8Array {
const salt = new Uint8Array(0); // Empty salt
const info = new TextEncoder().encode(`opacus-v1-${messageId}`);
return hkdf(sha256, sharedSecret, salt, info, keyLength);
}
// ✅ Both sender and recipient must use same parameters
const key1 = deriveMessageKey(secret, 'msg-123');
const key2 = deriveMessageKey(secret, 'msg-123');
// key1 === key2 ✓
Smart Contract Issues
❌ Transaction Reverted
Error Message
Error: Transaction reverted without a reason string
Solutions:
// 1. Estimate gas before sending
const gasEstimate = await contract.estimateGas.registerAgent(
metadata,
edPublicKey,
xPublicKey
);
console.log('Estimated gas:', gasEstimate.toString());
// Add 20% buffer
const gasLimit = gasEstimate * 120n / 100n;
// 2. Send with explicit gas limit
const tx = await contract.registerAgent(
metadata,
edPublicKey,
xPublicKey,
{ gasLimit }
);
// 3. Debug with hardhat node
// Run local node: npx hardhat node
// Deploy to local: npx hardhat run scripts/deploy.ts --network localhost
// Transaction errors will show detailed stack traces
❌ Insufficient Gas
// Check wallet balance
const balance = await provider.getBalance(wallet.address);
console.log('Balance:', ethers.formatEther(balance), '0G');
if (balance < ethers.parseEther('0.01')) {
console.error('Insufficient balance. Need at least 0.01 0G');
// Get testnet tokens from faucet
console.log('Faucet: https://faucet.0g.ai');
}
❌ Nonce Too Low
Error Message
Error: nonce has already been used
Solution:
// Get latest nonce
const nonce = await provider.getTransactionCount(wallet.address, 'latest');
// Send with explicit nonce
const tx = await contract.registerAgent(
metadata,
edPublicKey,
xPublicKey,
{ nonce }
);
// Or reset local cache
// Hardhat: npx hardhat clean
// Ethers: create new provider instance
Message Delivery
❌ Messages Not Being Received
Debug Checklist:
// 1. Verify recipient is connected
const recipientOnline = await client.isAgentOnline(recipientId);
console.log('Recipient online:', recipientOnline);
// 2. Check message was sent
const messageId = await client.sendMessage({
to: recipientId,
content: 'Test'
});
console.log('Message sent:', messageId);
// 3. Verify delivery
const status = await client.getMessageStatus(messageId);
console.log('Message status:', status);
// Possible: 'pending', 'delivered', 'failed'
// 4. Check recipient's message handler
client.onMessage((msg) => {
console.log('Received message:', msg);
// Make sure this handler is registered!
});
❌ Message Order Not Preserved
Solution:
// Use message sequencing
let messageSequence = 0;
async function sendOrdered(to: string, content: string) {
const seq = messageSequence++;
await client.sendMessage({
to,
content,
metadata: { sequence: seq }
});
}
// Recipient side: reorder messages
const messageBuffer = new Map();
let expectedSeq = 0;
client.onMessage((msg) => {
const seq = msg.metadata.sequence;
if (seq === expectedSeq) {
processMessage(msg);
expectedSeq++;
// Process buffered messages
while (messageBuffer.has(expectedSeq)) {
processMessage(messageBuffer.get(expectedSeq)!);
messageBuffer.delete(expectedSeq);
expectedSeq++;
}
} else {
// Buffer out-of-order message
messageBuffer.set(seq, msg);
}
});
Performance Issues
❌ High Latency
Diagnose:
// Measure end-to-end latency
const start = performance.now();
const messageId = await client.sendMessage({
to: recipientId,
content: 'Ping'
});
// Wait for delivery confirmation
await client.waitForDelivery(messageId);
const latency = performance.now() - start;
console.log('Round-trip latency:', latency, 'ms');
// Expected: <10ms on good network
// If >100ms, check:
// - Network connectivity (ping gateway)
// - Gateway load (check /metrics endpoint)
// - Message size (large messages take longer)
Solutions:
- Use connection pooling (see Performance Guide)
- Choose nearest gateway (geo-routing)
- Enable WebTransport for lower latency
- Compress large payloads
❌ Low Throughput
// Measure throughput
const count = 1000;
const start = Date.now();
for (let i = 0; i < count; i++) {
await client.sendMessage({
to: recipientId,
content: `Message ${i}`
});
}
const duration = (Date.now() - start) / 1000;
const throughput = count / duration;
console.log('Throughput:', throughput.toFixed(0), 'msg/s');
// Expected: >1000 msg/s
// If lower, try:
// - Batch messages
// - Use parallel processing
// - Increase connection pool size
Development & Debugging
Enable Debug Logging
// TypeScript: Enable verbose logging
const client = new OpacusClient({
gateway: 'gateway.opacus.ai',
logLevel: 'debug'
});
// Logs will show:
// [DEBUG] Connecting to gateway...
// [DEBUG] WebSocket opened
// [DEBUG] Sending message: {...}
// [DEBUG] Received message: {...}
// Rust: Enable tracing
use tracing_subscriber;
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.init();
// Or set environment variable
// RUST_LOG=debug cargo run
Test in Local Environment
# Start local 0G node
git clone https://github.com/0glabs/0g-chain
cd 0g-chain
make install
0gchaind start
# Deploy contracts locally
npx hardhat run scripts/deploy.ts --network localhost
# Start local gateway
cd gateway
cargo run -- --config config.local.toml
# Run SDK tests
npm test # TypeScript
cargo test # Rust
Common Error Codes
| Error Code | Description | Solution |
|---|---|---|
ECONNREFUSED |
Connection refused | Check gateway URL and firewall |
ETIMEOUT |
Connection timeout | Check network connectivity |
INVALID_SIGNATURE |
Signature verification failed | Verify keys and message format |
AGENT_NOT_FOUND |
Agent not registered | Register agent on-chain first |
INSUFFICIENT_GAS |
Not enough gas for transaction | Add more 0G tokens to wallet |
NONCE_TOO_LOW |
Transaction nonce already used | Get latest nonce or reset cache |
DECRYPTION_FAILED |
Cannot decrypt message | Check keys and encryption params |
RATE_LIMITED |
Too many requests | Implement backoff and retry |
Getting Help
FAQ
Q: Which SDK should I use?
A: Use TypeScript SDK for web/Node.js applications. Use Rust SDK for high-performance server applications or when you need QUIC protocol.
Q: How much do transactions cost?
A: Registration costs ~0.0001 0G, attestations ~0.00008 0G. Message routing is off-chain and free (only gateway bandwidth costs).
Q: Can I use testnet for production?
A: No, testnet is for development only. Testnet can be reset without notice. Use mainnet for production applications.
Q: How do I rotate keys?
A: Generate new keypair, update on-chain via agentRegistry.updateKeys(), revoke old attestations, create new ones.
Q: What happens if gateway goes down?
A: SDKs will automatically reconnect. Messages are queued during downtime. Use multiple gateway URLs for redundancy.
Q: How private are my messages?
A: Messages are end-to-end encrypted. Gateways cannot read content. Only sender and recipient can decrypt. See Security Model.
🎯 Still Stuck?
If you can't find a solution here:
- Check GitHub Issues for similar problems
- Open a GitHub Discussion for community help
- Email support@opacus.network for direct support
Related Documentation
- Getting Started - Setup guide
- Installation - Installation instructions
- Examples - Working code examples
- Security - Security best practices