.agents/skills/cline-sdk/references/agent/gotchas.md
If the agent keeps iterating without completing:
lifecycle: { completesRun: true } if you want the agent to explicitly finish.completesRun tools return successfully (not throwing errors).When a tool's execute function throws an exception, the SDK counts it as a "mistake." After too many mistakes, the agent stops with a mistake_limit finish reason.
Instead, return errors as structured data:
// Bad: throwing
execute: async (input) => {
throw new Error("File not found")
}
// Good: returning error data
execute: async (input) => {
return { error: "File not found", path: input.path }
}
run() for the first interaction. It sets up the conversation.continue() for subsequent messages. It appends to the existing conversation.run() a second time resets the conversation history.agent.hasRun to check which method to call.@cline/agents (and by extension, the Agent class) is browser-safe with no Node.js dependencies. However, @cline/core and ClineCore require Node.js 22+. If you import from @cline/sdk, you get everything including the Node-only code. For browser usage, import directly from @cline/agents:
import { Agent } from "@cline/agents"
AgentRuntimeConfig does not have a top-level onEvent field. Passing onEvent to new Agent({ onEvent: ... }) has no effect. There are two ways to receive events:
// Option 1: subscribe() - synchronous, best for UI streaming
const agent = new Agent({ ...config })
agent.subscribe((event) => {
if (event.type === "assistant-text-delta") {
process.stdout.write(event.text)
}
})
// Option 2: hooks.onEvent - awaited, best for async side effects
const agent = new Agent({
...config,
hooks: {
onEvent: async (event) => {
if (event.type === "assistant-text-delta") {
await logToService(event.text)
}
},
},
})
Both receive the same AgentRuntimeEvent types. Prefer subscribe() for streaming UI.
Register event listeners via subscribe() before calling run():
// Good: subscribe before run
agent.subscribe(handler)
const result = await agent.run(input)
// Bad: subscribing after run starts loses early events
const promise = agent.run(input)
agent.subscribe(handler) // may miss events
The model uses the tool's inputSchema to decide what arguments to pass. A vague or missing schema leads to incorrect tool calls.
z.enum() for fixed value sets, not free-form strings.describe() in Zod or description in JSON SchemaThe Agent holds all messages in memory. For long-running conversations, memory usage grows with each turn. Consider:
ClineCore with compaction for long sessionsresult.usage.totalInputTokens to track context growthLong-running tools should respect the abort signal:
execute: async (input, context) => {
for (const item of items) {
if (context.abortSignal?.aborted) {
return { partial: results, aborted: true }
}
results.push(await process(item))
}
return { results }
}
If you get authentication errors, check:
apiKey is set in the config or via environment variablesproviderId (e.g., Anthropic key for providerId: "anthropic")apiKey and baseUrl are setSee ../providers/REFERENCE.md for provider-specific setup.
api.md - Full API referencepatterns.md - Common patterns../tools/REFERENCE.md - Tool creation../clinecore/REFERENCE.md - Use ClineCore for persistence