Agent Native

check-crewai-api

Loads current CrewAI 1.15.x API surface for agents writing or debugging CrewAI code.

by CrewAIOfficial publisherNot reviewed

Identity proven by GitHub's verified organisation badge (crewAIInc)

Agents
Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode, claude.ai and API
License
MIT (permissive)
Pin
bebb6e8pinned Oct 9, 2026
Context cost
about 7,000 tokenswhen the skill loads
GitHub stars
44on crewaiinc/skills
Scripts
None found

What it does

“Current crewai 1.15.x API versus the 0.x API that coding assistants remember: imports, Agent/Task/Crew parameters and defaults, kickoff variants, CrewOutput fields, LLM model strings and provider extras, structured output (output_pydantic, response_model, response_format), guardrails, unified Memory, knowledge embedders, the `crewai create crew` wizard vs --classic, reset-memories flags, and removed features. Use when writing, reviewing, or debugging any crewai code, before trusting remembered crewai syntax, or when a user pastes errors like `cannot import name 'BaseTool' from 'crewai_tools'`, `cannot import name 'persist'`, `ShortTermMemory`, `Anthropic native provider not available`, `LiteLLM fallback package is not installed`, `got an unexpected keyword argument 'response_format'`, `OPENAI_API_KEY is required`, `If return type is annotated, it must be Tuple[bool, Any]`, or `result.pydantic is None`.”
The author's description, from SKILL.md

Generated summary from the skill's own text

Loads current CrewAI 1.15.x API surface for agents writing or debugging CrewAI code. Maps deprecated or changed parameters, imports, LLM setup, structured output patterns, and error messages from older versions to current equivalents with live-tested examples.

When to use it

  • Writing new CrewAI crews, agents, tasks, or flows with uncertain syntax
  • Debugging CrewAI errors like ImportError, TypeError, or ValidationError
  • Reviewing CrewAI code before trusting remembered syntax from tutorials
  • Migrating CrewAI 0.x code to 1.15.x

When not to

  • CrewAI version is not 1.15.x - re-verify with ask-docs skill instead
  • Installing or configuring CrewAI itself, not writing crew code
  • Troubleshooting non-API issues like network or authentication failures

Install

These commands install from the publisher's repository and need no account.

skills CLI

npx skills add crewaiinc/skills --skill check-crewai-api

Installs from the repository's default branch, which may have changed since the commit we checked.

GitHub CLI 2.90 or later

gh skill install crewaiinc/skills check-crewai-api@bebb6e8fc10bb8d07961969fb7bfd7d6091b421a

Installs the commit we checked (bebb6e8).

Copy by hand

Download the folder at bebb6e8 and put it in .claude/skills/check-crewai-api for Claude Code and in .agents/skills/check-crewai-api for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.

Skill folder on GitHub at bebb6e8

Copy for your agent

A prompt for any coding agent. It installs this skill from GitHub at the pinned commit, copies the files as they are, and runs nothing unless you ask.

Prompt

Install the agent skill "check-crewai-api" by CrewAI.

Source, pinned to commit bebb6e8fc10bb8d07961969fb7bfd7d6091b421a:
https://github.com/crewaiinc/skills/tree/bebb6e8fc10bb8d07961969fb7bfd7d6091b421a/skills/check-crewai-api

1. Download that folder at exactly this commit. Do not use the default branch.
2. Copy every file unchanged, keeping the folder structure, into .claude/skills/check-crewai-api/ for Claude Code and .agents/skills/check-crewai-api/ for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode. Create the folders if they do not exist.
3. Do not edit the files, and do not run any script in the folder unless I ask you to.
4. When you are done, list the files you copied and where you put them.

Listing and checks: https://www.agentnative.dev/skills/crewai/check-crewai-api

SKILL.md at the pin

---
name: check-crewai-api
description: "Current crewai 1.15.x API versus the 0.x API that coding assistants remember: imports, Agent/Task/Crew parameters and defaults, kickoff variants, CrewOutput fields, LLM model strings and provider extras, structured output (output_pydantic, response_model, response_format), guardrails, unified Memory, knowledge embedders, the `crewai create crew` wizard vs --classic, reset-memories flags, and removed features. Use when writing, reviewing, or debugging any crewai code, before trusting remembered crewai syntax, or when a user pastes errors like `cannot import name 'BaseTool' from 'crewai_tools'`, `cannot import name 'persist'`, `ShortTermMemory`, `Anthropic native provider not available`, `LiteLLM fallback package is not installed`, `got an unexpected keyword argument 'response_format'`, `OPENAI_API_KEY is required`, `If return type is annotated, it must be Tuple[bool, Any]`, or `result.pydantic is None`."
---

# Check CrewAI API

Load this before writing or reviewing crewai code: it maps what you probably remember to what crewai 1.15.x actually accepts.

Verified against crewai 1.15.22 and 1.15.23 on 2026-10-01.
Live-tested with real LLMs on 2026-10-01.
Run `crewai version` first; if the major/minor differs from 1.15, re-verify version-sensitive rows with the `ask-docs` skill before trusting them.

---

## 1. Three rules before you write anything

1. **Unknown keyword arguments are silently ignored.** `Agent`, `Task` and `Crew` accept and drop parameters that do not exist. `Crew(memory_config={...})` and `Task(response_format=Model)` construct fine and do nothing. A stale parameter name never raises, so check it exists:

   ```python
   from crewai import Agent, Crew, Task

   assert "response_model" in Task.model_fields      # real field
   assert "response_format" not in Task.model_fields  # silently dropped if passed
   assert "memory_config" not in Crew.model_fields    # silently dropped if passed
   print(sorted(Agent.model_fields)[:5])
   ```

2. **The installed source wins over any doc, tutorial, or memory.** That includes the `AGENTS.md` that `crewai create` writes into new projects. When a row below matters, check the installed signature (see [verify-installed-api.md](references/verify-installed-api.md)).
3. **Assert on structured output at the boundary.** Several mistakes below fail quietly with `out.pydantic is None` while the run reports success.

---

## 2. Imports

| You probably wrote | Current 1.15.x form | What the wrong form does |
|---|---|---|
| `from crewai_tools import BaseTool, tool` | `from crewai.tools import BaseTool, tool` | `ImportError: cannot import name 'BaseTool' from 'crewai_tools'` |
| `from crewai.flow.flow import Flow, start, listen, persist` | `from crewai.flow import Flow, start, listen, router, or_, and_, persist` | `ImportError: cannot import name 'persist'` |
| `from crewai.task import ConditionalTask` | `from crewai.tasks.conditional_task import ConditionalTask` | `ImportError` |
| `from crewai.memory import ShortTermMemory, LongTermMemory, EntityMemory` | `from crewai import Memory` | `ImportError` (the classes are gone) |
| `from crewai.utilities.events import crewai_event_bus` | `from crewai.events import BaseEventListener, crewai_event_bus` | `ModuleNotFoundError: No module named 'crewai.utilities.events'` |
| `from crewai_tools import CodeInterpreterTool` | No replacement in crewai - use a sandbox tool or service | `ImportError` (removed) |

The current top-level and project imports:

```python
from crewai import Agent, BaseLLM, Crew, CrewOutput, Flow, LLM, Memory, Process, Task, TaskOutput
from crewai.project import CrewBase, after_kickoff, agent, before_kickoff, crew, llm, task, tool
from crewai.tools import BaseTool, tool
from crewai.flow import Flow, and_, listen, or_, persist, router, start
from crewai.knowledge.source.string_knowledge_source import StringKnowledgeSource
from crewai.mcp import MCPServerHTTP, MCPServerSSE, MCPServerStdio
from crewai.events import BaseEventListener, crewai_event_bus
```

---

## 3. Agent, Task, Crew parameters and defaults

| Parameter | Current value | Note |
|---|---|---|
| `Task.expected_output` | **required** | `Task(description=...)` alone raises `ValidationError ... expected_output Field required` |
| `Agent.max_iter` | `25` | Do not assume 15 or 20 |
| `Agent.llm` | `None` -> resolved at construction | From env `MODEL`, then `MODEL_NAME`, then `OPENAI_MODEL_NAME`, else `gpt-4.1-mini` |
| `Agent.cache` | `True` | Only *permits* caching; it does not turn it on |
| `Crew.cache` | `False` | Tool-result caching is opt-in: set `Crew(cache=True)` |
| `Crew.memory` | `False` | |
| `Crew.process` | `Process.sequential` | Only `sequential` and `hierarchical` exist |
| `Task.guardrail_max_retries` | `3` | `max_retries` is deprecated (DeprecationWarning) |

| You probably wrote | Current form | What the wrong form does |
|---|---|---|
| `Process.consensual` | `Process.sequential` or `Process.hierarchical` | `AttributeError` |
| `Crew(process=Process.hierarchical, ...)` with no manager | add `manager_llm="openai/gpt-4.1-mini"` or `manager_agent=...` | `ValidationError: Attribute manager_llm or manager_agent is required` |
| `Crew(memory=True, memory_config={...})` | `Crew(memory=Memory(...))` | `memory_config` is silently ignored |
| `Agent(allow_code_execution=True)` / `code_execution_mode="unsafe"` | Remove; use a sandbox tool | Deprecated no-ops; `allow_code_execution=True` emits a `DeprecationWarning` |
| `Crew(function_calling_llm=...)` | Remove, or set `llm` per agent | Deprecated, `DeprecationWarning` |
| Two `async_execution=True` tasks with the same `agent` | One agent per concurrent async task | `RuntimeError: Executor is already running. Cannot invoke the same executor instance concurrently.` |

```python
from crewai import Agent, Crew, Process, Task

researcher = Agent(
    role="Researcher",
    goal="Find facts about {topic}",
    backstory="A careful researcher.",
    llm="openai/gpt-4.1-mini",
)
research = Task(
    description="List three facts about {topic}.",
    expected_output="Three bullet points.",
    agent=researcher,
)
crew = Crew(agents=[researcher], tasks=[research], process=Process.sequential, cache=True)
out = crew.kickoff(inputs={"topic": "honey bees"})
print(out.raw)
```

---

## 4. Running a crew and reading the result

| You probably wrote | Current form | What the wrong form does |
|---|---|---|
| `crew.kickoff("honey bees")` | `crew.kickoff(inputs={"topic": "honey bees"})` | `TypeError: inputs must be a dict or Mapping, got str` |
| `await crew.kickoff_async(...)` for native async | `await crew.akickoff(...)` | Works, but `kickoff_async` runs the sync `kickoff` in a thread (`asyncio.to_thread`) |
| `result.output` / `result.final_output` / `result.result` | `result.raw` | `AttributeError: 'CrewOutput' object has no attribute ...` |
| `crew.kickoff(...)` then `crew.kickoff_for_each(...)` on the same `Crew` | Build a fresh `Crew` for `kickoff_for_each` (or call it before any `kickoff`) | No error, but every run reuses the first kickoff's inputs: the per-run copies keep the already-interpolated task text |
| `Agent(mcps=["https://..."])` in a crew run with `await crew.akickoff()` | `mcps=[MCPServerHTTP(url="https://...")]` (from `crewai.mcp`) | No error, but the agent gets **zero** MCP tools: string refs resolve with `asyncio.run()`, which fails inside a running event loop (see connect-tools-and-mcp) |

`CrewOutput` fields: `raw`, `pydantic`, `json_dict`, `tasks_output` (a list of `TaskOutput`), `token_usage`. `out["field"]` reads from `pydantic`/`json_dict`. `kickoff_for_each(inputs=[...])` returns a list of `CrewOutput`.

`token_usage` is summed over each agent's `LLM` object for that object's whole lifetime, not per run. One `LLM(...)` instance shared by two agents counts every call twice, and one reused across crews carries the earlier crews' tokens (verified with `anthropic/claude-haiku-4-5`: a second one-call crew on the same `LLM` reported 148 tokens and 2 requests, a fresh `LLM` 73 and 1). For per-run cost, give each crew its own `LLM` objects, or count `LLMCallCompletedEvent`s (`event.usage`).

```python
import asyncio

from crewai import Agent, Crew, Task



def make_crew() -> Crew:
    writer = Agent(role="Writer", goal="Write", backstory="Writes short text.", llm="openai/gpt-4.1-mini")
    task = Task(description="One line on {topic}.", expected_output="One line.", agent=writer)
    return Crew(agents=[writer], tasks=[task])


out = make_crew().kickoff(inputs={"topic": "bees"})
print(out.raw, out.token_usage.total_tokens, [t.raw for t in out.tasks_output])

many = make_crew().kickoff_for_each(inputs=[{"topic": "bees"}, {"topic": "ants"}])
print([o.raw for o in many])


async def main():
    return await make_crew().akickoff(inputs={"topic": "wasps"})

print(asyncio.run(main()).raw)
```

---

## 5. LLMs, model strings, and provider extras

LiteLLM is no longer a dependency. `LLM(model=...)` is a factory that returns a native provider class; only the OpenAI SDK ships with core crewai.

| You probably wrote | Current form | What the wrong form does |
|---|---|---|
| `llm="anthropic/claude-sonnet-4-5"` with plain `crewai` installed | `uv add "crewai[anthropic]"`, then the same string | `ImportError: Anthropic native provider not available, to install: uv add "crewai[anthropic]"` |
| `llm="gemini/gemini-2.5-flash"` with plain `crewai` | `uv add "crewai[google-genai]"` | `ImportError: Google Gen AI native provider not available` |
| `llm="groq/llama-3.3-70b-versatile"` (or `mistral/...`, any non-native provider) | `uv add "crewai[litellm]"` | `ImportError: Unable to initialize LLM with model '...'. The model did not match any supported native provider (...), and the LiteLLM fallback package is not installed.` |
| No `llm=` and assuming `gpt-4` | Default is `gpt-4.1-mini`; set `MODEL=...` or `llm=` | |
| `llm.call(messages, response_format=Model)` | `llm.call(messages, response_model=Model)` | `TypeError: ... unexpected keyword argument 'response_format'. Did you mean 'response_model'?` |
| Custom `BaseLLM.call(self, messages, tools=None, callbacks=None, available_functions=None)` | Also accept `from_task=None, from_agent=None, response_model=None` (or `**kwargs`) | `TypeError: call() got an unexpected keyword argument 'from_task'` at kickoff |

Native providers: openai, anthropic/claude, azure, google/gemini, bedrock/aws, openrouter, deepseek, ollama, hosted_vllm, cerebras, dashscope, snowflake. Extras: `crewai[anthropic]`, `crewai[google-genai]`, `crewai[bedrock]`, `crewai[azure-ai-inference]`, `crewai[litellm]`. Construction never checks the key; kickoff does (`ValueError: OPENAI_API_KEY is required`, or `ANTHROPIC_API_KEY is required`; a wrong key gets the provider's `AuthenticationError ... 401`). The `crewai create` scaffolds never add a provider extra, even with `--provider anthropic/...`: run `uv add "crewai[anthropic]"` yourself.

```python
from crewai import LLM

gpt = LLM(model="openai/gpt-4.1-mini", temperature=0.2)  # core install
local = LLM(model="ollama/llama3")                        # core install, OpenAI-compatible route
claude = LLM(model="anthropic/claude-sonnet-4-5")         # needs: uv add "crewai[anthropic]"
print(type(gpt).__name__, type(local).__name__, type(claude).__name__)
```

A custom LLM subclasses `BaseLLM` and implements `call` with the full signature. If `call` forwards `messages` to a provider SDK, send only each message's `role` and `content`: crewai adds a `cache_breakpoint: True` key to some messages, and the Anthropic API rejects it with `400 ... messages.0.cache_breakpoint: Extra inputs are not permitted`.

```python
from typing import Any

from crewai import Agent, BaseLLM, Crew, Task


class EchoLLM(BaseLLM):
    def call(self, messages, tools=None, callbacks=None, available_functions=None,
             from_task=None, from_agent=None, response_model=None, **kwargs: Any) -> str:
        return "echo: " + str(messages[-1]["content"])[:40]


agent = Agent(role="Echo", goal="Echo", backstory="Echoes.", llm=EchoLLM(model="echo"))
task = Task(description="Say hi", expected_output="Anything", agent=agent)
print(Crew(agents=[agent], tasks=[task]).kickoff().raw)
```

---

## 6. Structured output

| Where | Use | Result lands in |
|---|---|---|
| `Task(output_pydantic=Model)` | Parsed model on the task and crew output | `out.pydantic`, `out["field"]` |
| `Task(output_json=Model)` | Dict output | `out.json_dict` |
| `Task(response_model=Model)` | Native provider structured output only | `out.raw` (JSON text); `out.pydantic` stays `None` |
| `Agent.kickoff(messages, response_format=Model)` | Standalone agent | `LiteAgentOutput.pydantic` |
| `llm.call(messages, response_model=Model)` | Direct LLM call | the parsed model |
| `LLM(model=..., response_format=Model)` | Constructor-level default | |

| You probably wrote | Current form | What the wrong form does |
|---|---|---|
| `Task(response_format=Model)` | `Task(output_pydantic=Model)` | Silently ignored |
| `Agent.kickoff(msg, response_model=Model)` | `Agent.kickoff(msg, response_format=Model)` | `TypeError: ... Did you mean 'response_format'?` |
| `Task(output_pydantic=M, output_json=M)` | Pick one | `ValidationError: Only one output type can be set` |
| `Task(output_pydantic=M, output_file="report.md")` expecting prose in the file | Drop `output_pydantic`, or render the model yourself | The file gets the model's JSON, whatever the extension |
| `output_file="/abs/path/report.md"` | A relative path (or `output_file="{out_dir}/report.md"` via inputs) | The leading `/` is stripped, so the file lands under the current directory |
| `output_pydantic: Report` in `tasks.yaml` | Set `output_pydantic=Report` in the `@task` method in Python | With no matching `@output_pydantic` class: `KeyError: 'Report'`. With one: silently dropped, `out.pydantic is None` |

```python
from pydantic import BaseModel

from crewai import Agent, Crew, Task


class Report(BaseModel):
    title: str
    points: list[str]


analyst = Agent(role="Analyst", goal="Summarise", backstory="Concise.", llm="openai/gpt-4.1-mini")
task = Task(description="Summarise {topic}.", expected_output="A title and points.",
            agent=analyst, output_pydantic=Report)
out = Crew(agents=[analyst], tasks=[task]).kickoff(inputs={"topic": "bees"})
assert out.pydantic is not None, "structured output was dropped"
print(out.pydantic.title, out["points"])

standalone = analyst.kickoff("Summarise bees.", response_format=Report)
print(standalone.pydantic)
```

In a `@CrewBase` project keep the model in Python even when the rest of the task is YAML:

```python
@task
def research_task(self) -> Task:
    return Task(config=self.tasks_config["research_task"], output_pydantic=Report)
```

---

## 7. Guardrails

A function guardrail takes one argument (`TaskOutput`) and returns a tuple `(bool, value)`. On `False` the value is fed back to the agent as the error and the task retries, up to `guardrail_max_retries` (default 3); then the task raises `Task failed guardrail validation after N retries`. A string guardrail is checked by an extra LLM call.

| You probably wrote | Current form | What the wrong form does |
|---|---|---|
| `def g(out) -> bool: return True` | `def g(out: TaskOutput) -> tuple[bool, Any]` returning `(True, out.raw)` | Annotated: `ValidationError: If return type is annotated, it must be Tuple[bool, Any]` at `Task(...)`. Unannotated: `TypeError: cannot unpack non-iterable bool object` at kickoff |
| `-> tuple[bool, object]` | `-> tuple[bool, Any]` (or `str`, `TaskOutput`) | Same `ValidationError` at `Task(...)` |
| `Task(max_retries=2)` | `Task(guardrail_max_retries=2)` | `DeprecationWarning` |
| Relying on `HallucinationGuardrail` | Write a function guardrail | In open-source crewai it is a no-op that always passes |

```python
from typing import Any

from crewai import Agent, Crew, Task, TaskOutput


def under_50_words(output: TaskOutput) -> tuple[bool, Any]:
    if len(output.raw.split()) > 50:
        return (False, "Keep it under 50 words.")
    return (True, output.raw)


writer = Agent(role="Writer", goal="Write", backstory="Brief.", llm="openai/gpt-4.1-mini")
task = Task(description="Describe {topic}.", expected_output="A short paragraph.", agent=writer,
            guardrails=[under_50_words, "Must not mention prices"], guardrail_max_retries=2)
print(Crew(agents=[writer], tasks=[task]).kickoff(inputs={"topic": "bees"}).raw)
```

`guardrails=[...]` runs each guardrail in order; a successful string result replaces `out.raw` for the next one.

---

## 8. Memory and knowledge

Memory is one `Memory` class (LanceDB storage). Its defaults call OpenAI: `text-embedding-3-large` for embeddings and `gpt-5.4-mini` for analysis. `Crew(memory=True)` analyses with the first agent's `llm` instead, and knowledge with no `embedder` embeds with `text-embedding-3-small`. Live with an OpenAI key: a `memory=True` crew stored a codename in one process and a new crew recalled it in the next; crew and agent knowledge both answered from the source.

| You probably wrote | Current form | What the wrong form does |
|---|---|---|
| `ShortTermMemory()`, `LongTermMemory()`, `EntityMemory()` | `Memory(...)` | `ImportError` |
| `Crew(memory=True)` with no (or an invalid) `OPENAI_API_KEY` | Set the key, or pass `Memory(llm=..., embedder=...)` | Crew **completes**; every save fails silently (only a `MemorySaveFailedEvent` is emitted) and nothing is stored |
| `memory.remember(...)` with no key | Same | `RuntimeError: Memory requires an embedder for vector search` (an invalid key: `AuthenticationError ... 401`) |
| `Agent(knowledge_sources=[...])` with no key or embedder, agent in a crew | Pass `embedder={...}` on the Agent or Crew | `ValueError: Invalid Knowledge Configuration: The OPENAI_API_KEY environment variable is not set.` at crew kickoff (an invalid key: `AuthenticationError ... 401`) |
| `agent.kickoff(...)` on an agent with `knowledge_sources` | Run the agent as a task in a `Crew`, or put the facts in the prompt | No error with or without a key: `Agent.kickoff()` never queries agent knowledge, so the answer ignores it |
| `Crew(knowledge_sources=[...])` with no key or embedder | Same | Logs `Failed to upsert documents`; crew runs with **no** knowledge |
| Changing the knowledge `embedder` after a run stored knowledge | `crewai reset-memories -kn` (or `crew.reset_memories(command_type="knowledge")`), then run | `Embedding function conflict: new: <x> vs persisted: openai`; the crew runs with no knowledge |
| Adding `embedder={"provider": "openai"}` after knowledge was stored with no `embedder` (or changing the OpenAI embedding model) | Same reset | `Failed to upsert documents: Collection expecting embedding with dimension of 1536, got 3072` (the explicit provider defaults to `text-embedding-3-large`); the crew answers without its knowledge |

The embedder config shape is `{"provider": <name>, "config": {...}}`; the OpenAI model key is `model_name`. `"model"` raises `TypeError: ... unexpected keyword argument 'model'` in `Memory`, and for knowledge it is silently ignored (the crew embeds with `text-embedding-3-large`). The `ollama` provider below also needs `uv add ollama` and a running Ollama server with the model pulled; without the package, crew knowledge is silently `None`. `{"provider": "onnx"}` is a local, key-free embedder that core crewai can already run (chromadb's `onnxruntime`; about 80 MB of model downloaded on first use) - the design-agent skill's memory-and-knowledge reference has working examples. For a deterministic embedder in tests, use a callable (`Memory`) or a custom class (knowledge) - see [verify-installed-api.md](references/verify-installed-api.md).

```python
from crewai import Agent, Crew, Memory, Task
from crewai.knowledge.source.string_knowledge_source import StringKnowledgeSource

embedder = {"provider": "ollama", "config": {"model_name": "nomic-embed-text"}}  # needs: uv add ollama
memory = Memory(llm="openai/gpt-4.1-mini", embedder=embedder)
facts = StringKnowledgeSource(content="acme was founded in 2020.")

agent = Agent(role="Analyst", goal="Answer", backstory="Uses knowledge.", llm="openai/gpt-4.1-mini")
task = Task(description="When was acme founded?", expected_output="A year.", agent=agent)
crew = Crew(agents=[agent], tasks=[task], memory=memory, knowledge_sources=[facts], embedder=embedder)
if crew.knowledge is None:
    print("knowledge failed to initialise - is the ollama package installed and the server running?")
```

Reset stored data from the project directory with `crewai reset-memories -m` (memory; errors with `Memory memory system is not initialized` if the crew has no memory), `-kn` (knowledge), `-akn` (agent knowledge), `-k` (latest kickoff outputs), `-a` (all). The old `-s`, `-l`, `-e` flags are hidden deprecated aliases that print a warning and reset the unified memory. Storage path and offline embedders: [verify-installed-api.md](references/verify-installed-api.md).

---

## 9. Project scaffold and CLI

| You probably wrote | Current form | What the wrong form does |
|---|---|---|
| `crewai create crew my_crew` expecting `crew.py` + YAML | `crewai create crew my_crew --classic` | Starts an interactive wizard that writes a JSON project (`crew.jsonc`, `agents/*.jsonc`); with no TTY it prints `Aborted!`. Scripts that only need a crew should use `--classic --skip-provider`. |
| `crewai run` on a JSON crew in CI or a script | For CI, create the crew with `crewai create crew <name> --classic` and run it with `uv run run_crew` | Opens a full-screen run view that waits for the user to quit, even with no TTY, so the command never exits. An environment variable, `CREWAI_DMN=1`, skipped the run view on crewai 1.15.x, but it is unsupported and undocumented; may change |
| Leaving `"memory": true` from the JSON scaffold with no `OPENAI_API_KEY` | Set the key, set `"memory": false`, or configure an embedder | The crew completes; memory recall and saves fail silently |
| `crewai run --inputs '{...}'` on a classic crew | Put inputs in `main.py` `run()` | `Error: --inputs is only supported for declarative flows and crews` |
| `crewai reset-memories -s -l -e` | `crewai reset-memories -m` | Deprecated aliases, warning printed |
| `return crew.kickoff(...)` from `main.run()` | Do not return the output | The `run_crew` script calls `sys.exit(<CrewOutput>)` and exits 1 after a successful run; `crewai run` prints `An error occurred while running the crew: ... non-zero exit status 1` |
| Trusting `crewai run`'s exit code in CI on a classic crew (`--classic`) or a Python flow | Run `uv run run_crew` (exit code propagates) or check the output | There `crewai run` exits 0 even when the crew or flow process failed; JSON (wizard) crews have no `run_crew` script, so create CI crews with `--classic` |

```bash
crewai version
crewai create crew research_crew --classic --skip-provider
cd research_crew && uv add "crewai[anthropic]"   # only if an agent uses an anthropic/ model
crewai install && crewai run
crewai reset-memories --help
```

The classic layout: `src/<name>/crew.py` (`@CrewBase` class), `src/<name>/config/agents.yaml` and `tasks.yaml` (keys match the `@agent`/`@task` method names), `main.py`, and `pyproject.toml` with `[tool.crewai] type = "crew"` and dependency `crewai[tools]`. In YAML, `llm: <name>` resolves to an `@llm` method of that name, otherwise it is used as a model string. `crewai create flow` also writes a `.env` with a placeholder `OPENAI_API_KEY` - replace it, never commit it.

---

## 10. Removed and deprecated

| Feature | Status in 1.15.22-1.15.23 | Do instead |
|---|---|---|
| `CodeInterpreterTool` | Removed from `crewai_tools` | Sandbox tool or service |
| `Agent(allow_code_execution=...)`, `code_execution_mode` | Deprecated no-ops | Same |
| `ShortTermMemory`, `LongTermMemory`, `EntityMemory`, `memory_config` | Removed / ignored | `Memory(...)` |
| `crewai.utilities.events` | Removed | `crewai.events` |
| `Crew(function_calling_llm=...)` | Deprecated | Per-agent `llm` |
| `Task(max_retries=...)` | Deprecated | `guardrail_max_retries` |
| `crewai.cli` module | Deprecation shim | `crewai_cli` (the CLI is its own package) |
| LiteLLM as a core dependency | Optional extra | `crewai[litellm]` only for non-native providers |

---

## 11. Common mistakes

| Symptom | Cause | Fix |
|---|---|---|
| A parameter has no effect and no error | Unknown kwargs are dropped by Agent/Task/Crew | Check `"name" in Task.model_fields` |
| `out.pydantic is None` | `output_pydantic` set in YAML, or `response_model` used instead of `output_pydantic` | Set `output_pydantic=Model` in Python; assert non-None |
| `ImportError: ... native provider not available` | Provider SDK extra missing | `uv add "crewai[anthropic]"` (or `[google-genai]`, `[bedrock]`, `[litellm]`) |
| `ValueError: OPENAI_API_KEY is required` at kickoff | No `llm=` set, so the default `gpt-4.1-mini` was used | Set `llm=` or `MODEL`, plus the provider key |
| Crew finishes but nothing was remembered | `memory=True` with no embedder key | Pass `Memory(embedder=...)` or set the key; listen for `MemorySaveFailedEvent` from `crewai.events` |
| Knowledge never appears in prompts | Crew-level knowledge failed to embed | Set `embedder=`; look for `Failed to upsert documents` |
| `TypeError: ... unexpected keyword argument 'from_task'` | Custom `BaseLLM.call` with the old signature | Accept `from_task`, `from_agent`, `response_model`, `**kwargs` |
| `Tuple[bool, Any]` ValidationError at `Task(...)` | Guardrail annotated `-> bool` or `tuple[bool, object]` | Annotate `-> tuple[bool, Any]` and return a tuple |
| `crewai create crew` hangs or prints `Aborted!` | It is an interactive JSON wizard now | Add `--classic` for the `@CrewBase` layout; use it for CI and scripts |
| `crewai run` never returns on a JSON crew | The run view waits for a keypress | For CI, use `crewai create crew <name> --classic` plus `uv run run_crew` |
| `kickoff_for_each` results all answer the first topic | The `Crew` was already kicked off | Build a fresh `Crew` per batch |
| `token_usage` larger than the run could have used | An `LLM` object shared by several agents or reused across crews | One `LLM` per agent per crew, or count `LLMCallCompletedEvent`s |
| Agent never uses its MCP tools under `akickoff()` | `https://` string in `mcps` inside a running event loop | `MCPServerHTTP(url=...)` |
| `Embedding function conflict ... persisted: openai` | Knowledge was stored with another embedder | `crewai reset-memories -kn` |
| `Collection expecting embedding with dimension of 1536, got 3072` | Knowledge was stored with another OpenAI embedding model | `crewai reset-memories -kn` |
| `crewai run` reports `non-zero exit status 1` after the crew succeeds | `run()` returns the `CrewOutput` | Do not return it |

---

## 12. Checklist

- [ ] `crewai version` checked; rows re-verified if not 1.15.x
- [ ] Every `Agent`/`Task`/`Crew` kwarg exists in `model_fields`
- [ ] Tools import from `crewai.tools`, flow decorators and `persist` from `crewai.flow`
- [ ] Every task has `expected_output`
- [ ] Every non-OpenAI model string has its extra in `pyproject.toml`
- [ ] Structured output set in Python with `output_pydantic`, and `out.pydantic` asserted non-None
- [ ] Guardrails return `(bool, value)` and are annotated `tuple[bool, Any]` or not at all
- [ ] Memory and knowledge have an explicit embedder or a provider key in the environment
- [ ] No `CodeInterpreterTool`, `allow_code_execution`, `ShortTermMemory`, `memory_config`
- [ ] Classic projects created with `crewai create crew <name> --classic`; provider extra added with `uv add`
- [ ] CI and scripts use classic crews (`crewai create crew <name> --classic`) run with `uv run run_crew`

---

## References

- [Verify the installed API](references/verify-installed-api.md) - one-liners to check fields, defaults and signatures in the installed crewai; storage paths; an offline embedder for memory and knowledge
- [Source receipts](references/source-receipts.md) - the crewai source file and line behind each row, so you can re-check after an upgrade
- Public docs: https://docs.crewai.com

For related skills:

- **ask-docs** - query the live CrewAI docs when a row may have changed
- **getting-started** - project scaffolding and choosing crews vs flows
- **design-agent** / **design-task** - tuning agents and tasks once the API is right
- **build-flow** - Flow state, routers, `@persist`, human feedback
- **connect-tools-and-mcp** - `BaseTool`, `@tool`, `mcps=[...]`
- **test-crewai-project** - offline stub LLM and deterministic tests
- **deploy-to-amp** / **call-deployed-crew** - shipping and calling a crew

From crewaiinc/skills at bebb6e8, shown as plain text under its license: MIT (permissive). The license text is in LICENSE.

View SKILL.md on GitHub at bebb6e8

Files

3 files, 41 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.

Skill folder on GitHub at bebb6e8
All 13 skills from CrewAI

More from CrewAI

  • Invalid--Name

    This skill cannot be described because its name format is invalid and no functional description or instructions are provided in the skill metadata.

    Claude Code, Codex, Cursor +4 · about 0 tokens · pinned 6 days ago

  • minimal-skill

    This skill provides minimal functionality with only required configuration fields.

    Claude Code, Codex, Cursor +4 · about 0 tokens · pinned 6 days ago

  • valid-skill

    This skill provides comprehensive instructions for agents to follow steps effectively with additional context.

    Contains scripts

    Claude Code, Codex, Cursor +3 · about 47 tokens · pinned 6 days ago

  • ask-docs

    This skill queries the official CrewAI documentation to answer framework and platform questions.

    Claude Code, Codex, Cursor +4 · about 3.1K tokens · pinned 6 days ago

  • build-flow

    Provides comprehensive guidance for building CrewAI Flows on version 1.15.x, covering state management with Pydantic models, wiring methods with decorators…

    Claude Code, Codex, Cursor +4 · about 6.7K tokens · pinned 6 days ago

  • call-deployed-crew

    Guides how to call a CrewAI crew or flow deployed on AMP via HTTP by managing bearer tokens, required inputs, polling for results, and writing crews that…

    Claude Code, Codex, Cursor +4 · about 5.5K tokens · pinned 6 days ago

All AI and agents skills
  • connect-tools-and-mcp

    This skill teaches how to connect tools and MCP servers to CrewAI agents on version 1.15.x, covering custom Python tools, prebuilt crewai_tools, and MCP…

    by CrewAIOfficial publisher

    Claude Code, Codex, Cursor +4 · about 6.3K tokens · pinned 6 days ago

  • anthropic-administrator-automation

    This skill automates Anthropic Admin tasks by connecting to Composio's Anthropic Admin toolkit through Rube MCP.

    by ComposioOfficial publisher

    Agents not labelled yet · about 730 tokens · pinned 6 days ago

  • anthropic_administrator-automation

    This skill automates Anthropic Admin operations including API key management, usage monitoring, workspace administration, and organization settings through…

    by ComposioOfficial publisher

    Agents not labelled yet · about 1K tokens · pinned 6 days ago

  • acquiring-skills

    Enables the agent to discover and install skills from multiple registries including Hermes Skills Hub, ClawHub, GitHub, and community repositories.

    by LettaOfficial publisher

    Agents not labelled yet · about 3.1K tokens · pinned 6 days ago

  • llm-to-bedrock

    This skill migrates code from OpenAI, Gemini, or Anthropic APIs to Amazon Bedrock by assessing the codebase, rewriting SDK calls, and evaluating output quality.

    by Amazon (AWS)Official publisher

    Contains scripts

    Claude Code · about 15.6K tokens · pinned 6 days ago

  • redis-semantic-cache

    This skill guides agents to implement semantic caching for LLM responses using Redis Cloud's LangCache service.

    by RedisOfficial publisher

    Claude Code, Codex, Cursor +4 · about 880 tokens · pinned 6 days ago

Open roles at CrewAI

The skills listed here are third-party software, collected from public GitHub repositories as a catalog of recent skills and provided as is. Agent Native does not write or control them and is not responsible for any harm, loss or damage caused by installing or using them. Labels and checks describe what we saw at one commit and are not a guarantee. Read a skill's files before you run it. Terms of use