> ## Documentation Index
> Fetch the complete documentation index at: https://docs.corti.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Common pitfalls

> Avoid the most frequent mistakes when using the Corti Agent SDK.

<Warning>
  The Corti Agent SDK is in **alpha v2 private preview**. The API may change between releases. Contact [help@corti.ai](mailto:help@corti.ai) to request access.
</Warning>

## Timeouts

**Orchestrators time out at 60 seconds by default.** Anything that fans out to sub-agents or MCP connectors should pass `timeoutInSeconds: 180` (or higher) to `run()` or `sendText()`. The default 60-second timeout is fine for single-agent calls but will cut off orchestrators that delegate to multiple connectors.

<CodeGroup>
  ```ts title="JavaScript" theme={null}
  // Bad: default 60s timeout may be too short for orchestrators
  const reply = await handle.run("Complex clinical question...");

  // Good: raise the timeout for orchestrators
  const reply = await handle.run("Complex clinical question...", { timeoutInSeconds: 180 });
  ```

  ```python title="Python" theme={null}
  # Bad: default 60s timeout may be too short for orchestrators
  reply = await agent.run("Complex clinical question...")

  # Good: raise the timeout for orchestrators
  reply = await agent.run("Complex clinical question...", timeout_in_seconds=180)
  ```
</CodeGroup>

## Context IDs

**You do not need to manage context IDs.** Keep the `AgentContext` object in memory across turns. The SDK tracks the thread automatically. Only use `handle.getContext(id)` / `agent.get_context(id)` when resuming after a process restart.

**`ctx.id` is `undefined` / `None` until the first turn completes.** Do not persist it before then.

**`createContext()` does not accept a context ID.** Passing `{ contextId }` will be silently ignored. Use `handle.getContext(contextId)` / `agent.get_context(context_id)` to resume an existing thread.

## Workflow vs stateGraph

**`workflow` is linear, `stateGraph` has cycles.** Do not use `workflow` for branching that revisits earlier agents. If you need loops, conditional routing based on accumulated state, or a reviewer-approval cycle, use `stateGraph` instead.

## Parallel failures

**`parallel` swallows individual failures.** Inspect `rejected` if you need fail-fast semantics. When used inside a `workflow`, if all parallel steps fail, the workflow throws.

<CodeGroup>
  ```ts title="JavaScript" theme={null}
  const result = await parallel([a, b, c]).run(input);
  if (result.rejected.length > 0) {
    console.warn(`${result.rejected.length} steps failed`);
    for (const err of result.rejected) {
      console.error(err);
    }
  }
  ```

  ```python title="Python" theme={null}
  result = await parallel([a, b, c]).run(input)
  if result.rejected:
      print(f"{len(result.rejected)} steps failed")
      for err in result.rejected:
          print(err)
  ```
</CodeGroup>

## Persistent agents

**Persistent agents accumulate.** The default is `ephemeral`; only use `persistent` if you truly need the agent to survive restarts, and always delete what you no longer need.

<CodeGroup>
  ```ts title="JavaScript" theme={null}
  const agent = await client.agents.create({ name: "my-bot", description: "...", lifecycle: "persistent" });
  // ... use it across sessions ...
  // Clean up when done
  const handle = new AgentHandle(agent, client);
  await handle.delete();
  ```

  ```python title="Python" theme={null}
  agent = await agents.create(name="my-bot", description="...", lifecycle="persistent")
  # ... use it across sessions ...
  # Clean up when done
  await agent.delete()
  ```
</CodeGroup>

## ESM only (TypeScript)

The TypeScript package is ESM-only (`"type": "module"`). Import with `import`, not `require`. For CommonJS projects, use dynamic `import()` or set your project to ESM.

## Self-sufficient prompts

**Sub-agents that ask for clarification will stall an orchestrator.** Prompt worker agents with instructions like "Never ask for clarification" so they always produce output. An orchestrator that delegates to a sub-agent which asks a question will hang waiting for user input that never comes.

## Credential keys must match connector names (Python only)

In the Python SDK (v1 architecture), the credential key in the `CredentialStore` must match the connector's `name`:

```python theme={null}
# The connector name "my-mcp" must match the credential key
agent = await agents.create(
    name="auth-demo",
    description="...",
    connectors=[connectors.mcp(mcp_url=MCP_URL, name="my-mcp", auth_type="bearer")],
)

ctx = agent.create_context(credentials={
    "my-mcp": {"type": "token", "token": MCP_TOKEN},  # key matches connector name
})
```

In the TypeScript v2 SDK, auth is configured on the connector itself at creation time, so this pattern does not apply.

## Streaming field names

**Use `event.message`, not `event.msg`.** The streaming event field for message updates is `message`. The field for artifact updates is `artifactUpdate`, not `artifact`.

<CodeGroup>
  ```ts title="JavaScript" theme={null}
  // Correct
  if (event.message) { /* ... */ }
  if (event.artifactUpdate) { /* ... */ }

  // Wrong: these fields do not exist
  if (event.msg) { /* ... */ }
  if (event.artifact) { /* ... */ }
  ```
</CodeGroup>
