Troubleshooting Guide
This guide covers common issues and solutions when developing with Spritz.
Authentication Issues
"Session not found" or "Unauthorized" Errors
Symptoms:
- API returns 401 Unauthorized
- User appears logged out unexpectedly
- Actions fail with session errors
Solutions:
- Check credentials mode in fetch requests:
// ❌ Wrong - cookies won't be sent
fetch('/api/agents');
// ✅ Correct - include credentials
fetch('/api/agents', { credentials: 'include' });
- Verify cookie domain settings:
// In your auth verification, ensure cookies are set correctly
cookies().set('session', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 60 * 60 * 24 * 7, // 7 days
path: '/',
});
- Check session expiration:
// Sessions expire after 7 days by default
// Check JWT expiration
import { jwtVerify } from 'jose';
const { payload } = await jwtVerify(token, secretKey);
if (payload.exp && payload.exp < Date.now() / 1000) {
console.log('Session expired');
}
Passkey Authentication Fails
Symptoms:
- "Passkey not found" error
- WebAuthn assertion fails
- Cross-device authentication issues
Solutions:
- Verify origin matches:
// Origin must match exactly
const expectedOrigin = process.env.NEXT_PUBLIC_APP_URL;
const { origin } = new URL(clientData.origin);
if (origin !== expectedOrigin) {
throw new Error(`Origin mismatch: ${origin} !== ${expectedOrigin}`);
}
- Check RP ID configuration:
// RP ID should be the domain without protocol
const rpId = 'spritz.chat'; // Not 'https://spritz.chat'
- Handle cross-device passkeys:
// Some passkeys are device-bound, others are synced
// Check the authenticator flags
const isDeviceBound = !(flags & 0x01); // UP flag check
SIWE Signature Verification Fails
Symptoms:
- "Invalid signature" error
- Nonce mismatch
- Message format errors
Solutions:
- Verify message format:
import { SiweMessage } from 'siwe';
// Ensure message is prepared correctly
const message = new SiweMessage({
domain: window.location.host,
address: address,
statement: 'Sign in to Spritz',
uri: window.location.origin,
version: '1',
chainId: 8453,
nonce: nonce,
});
// Use prepareMessage() not toString()
const messageToSign = message.prepareMessage();
- Check nonce validity:
// Nonce is returned with the SIWE message from the server
// and must be used within 5 minutes
const { message, nonce } = await fetch(`/api/auth/verify?address=${address}`).then(r => r.json());
// The nonce is already embedded in the message
// Just sign and submit within the validity window
Database Issues
"Relation does not exist" Error
Symptoms:
ERROR: relation "table_name" does not exist- Queries fail after deployment
- Missing tables or columns
Solutions:
- Run migrations in order:
# Migrations must be run alphabetically
ls -1 migrations/*.sql | sort | while read f; do
psql -d spritz -f "$f"
done
- Check for missing dependencies:
-- Some tables depend on others
-- Run core tables first:
-- 1. agents.sql
-- 2. embeddings.sql
-- Then dependent tables:
-- 3. agents_x402.sql (requires agents)
- Verify schema:
-- Check if table exists
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'your_table'
);
Vector Search Not Working
Symptoms:
- RAG returns no results
- Similarity search fails
pgvectorextension errors
Solutions:
- Enable pgvector extension:
-- Must be enabled before creating vector columns
CREATE EXTENSION IF NOT EXISTS vector;
- Check embedding dimensions:
-- Gemini embeddings are 768 dimensions
ALTER TABLE embeddings
ALTER COLUMN embedding TYPE vector(768);
- Create index for performance:
-- Use IVFFlat for larger datasets
CREATE INDEX ON embeddings
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
RLS Policy Blocks Access
Symptoms:
- Queries return empty results
- "Permission denied" errors
- Data visible in admin but not API
Solutions:
- Check RLS is enabled:
-- Verify RLS status
SELECT tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public';
- Debug policy evaluation:
-- Temporarily check what user sees
SET ROLE authenticated;
SET request.jwt.claim.sub = 'user_address';
SELECT * FROM agents; -- See what RLS allows
RESET ROLE;
- Common policy pattern:
-- Ensure policy covers your use case
CREATE POLICY "Users can view own agents"
ON agents FOR SELECT
USING (owner_address = auth.jwt() ->> 'sub');
Messaging Issues
Logos Messaging (Waku) - Messages Not Delivering
Terminology
Spritz uses Logos Messaging (built on the Waku protocol) for decentralized peer-to-peer messaging. In code, you may see references to @waku/sdk as Logos Messaging uses Waku under the hood.
Symptoms:
- Messages sent but not received
- Intermittent message loss
- High latency
Solutions:
- Check content topic format:
// Content topics must follow the format
const contentTopic = `/spritz/1/dm-${sortedAddresses}/proto`;
// Addresses must be sorted consistently
const sortedAddresses = [addr1, addr2].sort().join('-');
- Verify peer connections:
import { waitForRemotePeer, Protocols } from '@waku/sdk';
// Wait for peers before sending
await waitForRemotePeer(node, [
Protocols.Filter,
Protocols.LightPush,
]);
console.log('Connected peers:', node.libp2p.getPeers().length);
- Handle message encoding:
// Messages must be properly encoded
const encoder = createEncoder({
contentTopic,
pubsubTopic: '/waku/2/default-waku/proto',
});
const payload = proto.Message.encode({
content: message,
timestamp: Date.now(),
sender: address,
}).finish();
Realtime Subscriptions Not Working
Symptoms:
- Supabase realtime events not firing
- Channel joins fail
- Updates not reflected in UI
Solutions:
- Enable realtime for table:
-- In Supabase dashboard or via SQL
ALTER PUBLICATION supabase_realtime ADD TABLE your_table;
- Check channel subscription:
const channel = supabase
.channel('room1')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'messages',
filter: `channel_id=eq.${channelId}`,
},
(payload) => {
console.log('Change received:', payload);
}
)
.subscribe((status) => {
if (status === 'SUBSCRIBED') {
console.log('Subscribed!');
}
});
- Verify RLS allows subscription:
-- RLS policies also apply to realtime
-- Ensure SELECT policy exists
CREATE POLICY "Allow reading messages"
ON messages FOR SELECT
USING (true); -- Or your specific condition
Video Call Issues
Huddle01 Room Creation Fails
Symptoms:
- "Invalid API key" error
- Room creation returns null
- Token generation fails
Solutions:
- Check environment variables:
# Required variables
HUDDLE01_API_KEY=your_api_key
HUDDLE01_PROJECT_ID=your_project_id
- Verify API key scope:
// API key must have room creation permissions
const response = await fetch('https://api.huddle01.com/api/v1/create-room', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.HUDDLE01_API_KEY,
},
body: JSON.stringify({
title: roomTitle,
roomLocked: false,
}),
});
if (!response.ok) {
const error = await response.json();
console.error('Huddle01 error:', error);
}
- Handle rate limits:
// Huddle01 has rate limits
// Implement exponential backoff
const delay = Math.min(1000 * Math.pow(2, retryCount), 10000);
await new Promise(r => setTimeout(r, delay));
Screen Sharing Not Working
Symptoms:
- Screen share button disabled
- "NotAllowedError" in console
- Black screen on share
Solutions:
- Check HTTPS:
// Screen sharing requires secure context
if (!window.isSecureContext) {
console.error('Screen sharing requires HTTPS');
}
- Handle permissions:
try {
const stream = await navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true,
});
} catch (err) {
if (err.name === 'NotAllowedError') {
// User denied permission
} else if (err.name === 'NotFoundError') {
// No screen sharing available
}
}
Deployment Issues
Build Fails with Type Errors
Symptoms:
tscreports errors- Import resolution fails
- Module not found
Solutions:
- Check TypeScript version:
{
"devDependencies": {
"typescript": "^5.0.0"
}
}
- Verify path aliases:
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
}
}
- Clear cache and rebuild:
rm -rf .next node_modules/.cache
npm run build
Environment Variables Not Loading
Symptoms:
undefinedfor env vars- Different behavior in dev vs prod
- Client-side vars not available
Solutions:
- Use correct prefix:
# Server-side only
DATABASE_URL=postgres://...
# Available in browser (Next.js)
NEXT_PUBLIC_APP_URL=https://app.spritz.chat
- Check .env file location:
project/
├── .env.local # Local overrides (gitignored)
├── .env.development # Dev defaults
├── .env.production # Prod defaults
└── .env # Shared defaults
- Verify in runtime:
// Debug env loading
console.log('Env check:', {
hasDbUrl: !!process.env.DATABASE_URL,
appUrl: process.env.NEXT_PUBLIC_APP_URL,
});
Performance Issues
Slow API Responses
Symptoms:
- Requests take > 1 second
- Timeouts on complex queries
- High database CPU
Solutions:
- Add database indexes:
-- Index frequently queried columns
CREATE INDEX idx_agents_owner ON agents(owner_address);
CREATE INDEX idx_messages_channel ON channel_messages(channel_id);
CREATE INDEX idx_messages_created ON channel_messages(created_at DESC);
- Use pagination:
// Don't fetch all records
const { data } = await supabase
.from('messages')
.select('*')
.eq('channel_id', channelId)
.order('created_at', { ascending: false })
.range(0, 49); // Fetch 50 at a time
- Enable connection pooling:
// Use connection string with pooler
const connectionString = process.env.DATABASE_URL; // Should include ?pgbouncer=true
Memory Leaks in Development
Symptoms:
- Node process grows continuously
- Browser tab becomes slow
- "JavaScript heap out of memory"
Solutions:
- Clean up subscriptions:
useEffect(() => {
const channel = supabase.channel('updates');
return () => {
supabase.removeChannel(channel);
};
}, []);
- Unsubscribe from Waku:
useEffect(() => {
const unsubscribe = node.filter.subscribe(/* ... */);
return () => {
unsubscribe();
};
}, []);
Common Error Code Reference
For a complete list of API error codes and their meanings, see the Error Codes Reference.
| HTTP Status | Common Cause | Quick Fix |
|---|---|---|
| 401 | Session expired or missing credentials | Ensure credentials: 'include' in fetch |
| 402 | x402 payment required | Provide valid X-Payment header |
| 403 | Access denied / RLS policy | Check permissions and ownership |
| 404 | Resource not found | Verify ID exists and is accessible |
| 429 | Rate limit exceeded | Implement exponential backoff |
| 500 | Server error | Check logs and retry |
Getting Help
If your issue isn't covered here:
- Check the FAQ for common questions
- Review Error Codes for API errors
- Search existing issues on GitHub
- Join the community channels for support
When reporting issues, include:
- Error message and stack trace
- Steps to reproduce
- Environment (OS, Node version, browser)
- Relevant code snippets