7.8 KiB
Agent Troubleshooting
Common issues and solutions for Cloudflare Agents.
Connection Issues
"WebSocket connection failed"
Symptoms: Client cannot connect to agent.
Causes & Solutions:
-
Worker not deployed
wrangler deployments list wrangler deploy # If not deployed -
Wrong URL path
// Ensure your routing handles the agent path // Client: new WebSocket("wss://my-worker.workers.dev/agent/user123"); // Worker must route to agent: if (url.pathname.startsWith("/agent/")) { const id = url.pathname.split("/")[2]; return env.AGENT.get(env.AGENT.idFromName(id)).fetch(request); } -
CORS issues (browser clients) Agents handle WebSocket upgrades automatically, but ensure your entry worker doesn't block the request.
"Connection closed unexpectedly"
-
Agent threw an error
wrangler tail # Check for exceptions -
Message handler crashed
async onMessage(connection: Connection, message: string) { try { // Your logic } catch (error) { console.error("Message handling error:", error); connection.send(JSON.stringify({ type: "error", message: error.message })); } } -
Hibernation woke agent with stale connection Ensure you handle reconnection gracefully in client code.
State Issues
"State not persisting"
Causes:
-
Didn't call
setState()// Wrong - direct mutation doesn't persist this.state.messages.push(newMessage); // Correct - use setState this.setState({ ...this.state, messages: [...this.state.messages, newMessage], }); -
Agent crashed before state saved
setState()is durable, but if agent crashes during processing beforesetState(), changes are lost. -
Wrong agent instance Each unique ID gets a separate agent. Ensure clients connect to the same ID.
"State out of sync between clients"
setState() automatically syncs to all connected clients via onStateUpdate(). If sync isn't working:
-
Check
onStateUpdateis implementedonStateUpdate(state: State, source: string) { // This fires when state changes from any source console.log("State updated:", state, "from:", source); } -
Client not listening for state updates
// React hook handles this automatically const { state } = useAgent({ agent: "my-agent", name: id }); // Manual WebSocket - listen for state messages ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === "state_update") { updateLocalState(data.state); } };
"State too large" / Performance issues
State is serialized as JSON. Keep it small:
// Bad - storing everything in state
interface State {
allMessages: Message[]; // Could be thousands
allDocuments: Document[];
}
// Good - state for hot data, SQL for cold
interface State {
recentMessages: Message[]; // Last 50 only
currentDocument: Document | null;
}
// Store full history in SQL
await this.sql`INSERT INTO messages ...`;
SQL Issues
"no such table"
Table not created. Create in onStart():
async onStart() {
await this.sql`
CREATE TABLE IF NOT EXISTS messages (
id TEXT PRIMARY KEY,
content TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`;
}
"SQL logic error"
Check your query syntax. Use tagged templates correctly:
// Wrong - string interpolation (SQL injection risk!)
await this.sql`SELECT * FROM users WHERE id = '${userId}'`;
// Correct - parameterized query
await this.sql`SELECT * FROM users WHERE id = ${userId}`;
SQL query returns empty
- Wrong table name
- Data in different agent instance (each agent ID has isolated storage)
- Query conditions don't match
Debug:
const tables = await this.sql`
SELECT name FROM sqlite_master WHERE type='table'
`;
console.log("Tables:", tables);
const count = await this.sql`SELECT COUNT(*) as count FROM messages`;
console.log("Message count:", count);
Scheduled Task Issues
"Task never fires"
-
Method name mismatch
// Schedule references method that must exist await this.schedule(60, "sendReminder", { ... }); // Method must be defined on the class async sendReminder(data: any) { // This method MUST exist } -
Cron syntax error
// Invalid cron await this.schedule("every 5 minutes", "task", {}); // Wrong // Valid cron await this.schedule("*/5 * * * *", "task", {}); // Every 5 minutes -
Task was cancelled
const schedules = await this.getSchedules(); console.log("Active schedules:", schedules);
"Task fires multiple times"
If you schedule in onStart() without checking:
async onStart() {
// Bad - schedules new task every time agent wakes
await this.schedule("0 9 * * *", "dailyTask", {});
// Good - check first
const schedules = await this.getSchedules();
const hasDaily = schedules.some(s => s.callback === "dailyTask");
if (!hasDaily) {
await this.schedule("0 9 * * *", "dailyTask", {});
}
}
Deployment Issues
"Class MyAgent is not exported"
// src/index.ts - Must export the class
export { MyAgent } from "./agent";
// Or if defined in same file
export class MyAgent extends Agent { ... }
"Durable Object not found"
Check wrangler.toml:
[durable_objects]
bindings = [{ name = "AGENT", class_name = "MyAgent" }]
[[migrations]]
tag = "v1"
new_classes = ["MyAgent"]
"Migration required"
When adding new Durable Object classes:
[[migrations]]
tag = "v2" # Increment from previous
new_classes = ["NewAgentClass"]
# Or for renames
# renamed_classes = [{ from = "OldName", to = "NewName" }]
AI Integration Issues
"AI binding not found"
Add to wrangler.toml:
[ai]
binding = "AI"
"Model not found" / "Rate limited"
// Check model name is correct
const response = await this.env.AI.run(
"@cf/meta/llama-3-8b-instruct", // Exact model name
{ messages: [...] }
);
// Handle rate limits
try {
const response = await this.env.AI.run(...);
} catch (error) {
if (error.message.includes("rate limit")) {
// Retry with backoff or use queue
}
}
"Streaming not working"
// Enable streaming
const stream = await this.env.AI.run("@cf/meta/llama-3-8b-instruct", {
messages: [...],
stream: true, // Must be true
});
// Iterate over stream
for await (const chunk of stream) {
connection.send(JSON.stringify({ type: "chunk", content: chunk.response }));
}
Debugging Tips
Enable Verbose Logging
export class MyAgent extends Agent<Env, State> {
async onStart() {
console.log("Agent starting, state:", JSON.stringify(this.state));
}
async onConnect(connection: Connection) {
console.log("Client connected:", connection.id);
}
async onMessage(connection: Connection, message: string) {
console.log("Received message:", message);
// ... handle
console.log("State after:", JSON.stringify(this.state));
}
async onClose(connection: Connection) {
console.log("Client disconnected:", connection.id);
}
}
View logs:
wrangler tail --format pretty
Test Locally First
npm start
# Connect with test client or use browser console:
# new WebSocket("ws://localhost:8787/agent/test")
Inspect State
Add a debug endpoint:
async onRequest(request: Request) {
const url = new URL(request.url);
if (url.pathname === "/debug") {
return Response.json({
state: this.state,
schedules: await this.getSchedules(),
});
}
return new Response("Not found", { status: 404 });
}