# Agent Gotchas

## Agent Loop Never Stops

If the agent keeps iterating without completing:

- Make sure at least one tool has `lifecycle: { completesRun: true }` if you want the agent to explicitly finish.
- Without any tools, the agent will complete after the model returns text without tool calls.
- If using tools, ensure the system prompt guides the model toward calling the completion tool when done.
- Check that `completesRun` tools return successfully (not throwing errors).

## Tool Errors Become Error Results

When a direct Agent tool's `execute` function throws an exception, the runtime catches it and returns a `tool-result` part with `isError: true`. ClineCore wraps the direct runtime and can treat repeated failed tool turns as recoverable mistakes.

Instead, return errors as structured data:

```typescript
// 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() vs continue()

- `run(input)` and `continue(input?)` both execute against the current in-memory message history.
- Neither method resets history. Use `restore(messages)` to replace history, or create a new `Agent` for a fresh conversation.
- There is no `agent.hasRun` property. Track first-run state in your app if you need separate UI behavior.

## Browser Compatibility

`@cline/agents` is browser-safe. `@cline/sdk` re-exports `@cline/core`, which is Node-oriented. For browser usage, import directly from `@cline/agents`:

```typescript
import { Agent } from "@cline/agents"
```

## No Top-Level onEvent on Agent Config

`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:

```typescript
// 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.

## Event Listener Timing

Register event listeners via `subscribe()` before calling `run()`:

```typescript
// 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
```

## Tool Input Schema Matters

The model uses the tool's `inputSchema` to decide what arguments to pass. A vague or missing schema leads to incorrect tool calls.

- Use `z.enum()` for fixed value sets, not free-form strings
- Describe every property with `.describe()` in Zod or `description` in JSON Schema
- Include constraints (rate limits, max values) in the tool description

## Memory and Long Conversations

The Agent holds all messages in memory. For long-running conversations, memory usage grows with each turn. Consider:

- Using `ClineCore` with compaction for long sessions
- Periodically creating a new agent with a summary of the conversation
- Monitoring `result.usage.inputTokens` and `result.messages.length` to track context growth

## Abort Signal Handling in Tools

Long-running tools should respect the abort signal:

```typescript
execute: async (input, context) => {
  for (const item of items) {
    if (context.signal?.aborted) {
      return { partial: results, aborted: true }
    }
    results.push(await process(item))
  }
  return { results }
}
```

## Provider API Key

If you get authentication errors, check:

- `apiKey` is set in the config or via environment variables
- The key matches the `providerId` (e.g., Anthropic key for `providerId: "anthropic"`)
- For OpenAI-compatible providers, both `apiKey` and `baseUrl` are set

See `../providers/REFERENCE.md` for provider-specific setup.

## See Also

- `api.md` - Full API reference
- `patterns.md` - Common patterns
- `../tools/REFERENCE.md` - Tool creation
- `../clinecore/REFERENCE.md` - Use ClineCore for persistence
