run-llama/llama_index · warning · ValueError

'handoff' is a reserved tool name. Please use a different na

Error message

'handoff' is a reserved tool name. Please use a different name.

What it means

MutableMappingKVStore.persist unconditionally raises NotImplementedError; it exists only so type checkers accept the method on the base class. Persistence is a capability of concrete dict-backed stores like SimpleKVStore, which implements persist(persist_path, fs) to write JSON; calling it on the base (or a subclass that inherits it) means you asked a non-persistable store to persist.

Source

Thrown at llama-index-core/llama_index/core/agent/workflow/base_agent.py:228

        """
        Validate tools.

        If tools are not of type BaseTool, they will be converted to FunctionTools.
        This assumes the inputs are tools or callable functions.
        """
        if v is None:
            return None

        validated_tools: List[BaseTool] = []
        for tool in v:
            if not isinstance(tool, BaseTool):
                validated_tools.append(FunctionTool.from_defaults(tool))
            else:
                validated_tools.append(tool)

        for tool in validated_tools:
            if tool.metadata.name == "handoff":
                raise ValueError(
                    "'handoff' is a reserved tool name. Please use a different name."
                )

        return validated_tools  # type: ignore[return-value]

    def _get_prompts(self) -> PromptDictType:
        """Get prompts."""
        return {}

    def _get_prompt_modules(self) -> PromptMixinType:
        """Get prompt sub-modules."""
        return {}

    def _update_prompts(self, prompts_dict: PromptDictType) -> None:
        """Update prompts."""

    @abstractmethod
    async def take_step(

View on GitHub (pinned to afd0fef371)

Solutions

  1. Use SimpleKVStore (or a subclass that implements persist) when you need to persist to disk.
  2. Override persist in your custom store: serialize self._data with json.dump to persist_path.
  3. Branch on capability: if isinstance(store, SimpleKVStore): store.persist(...) else: <export via your own mechanism>.
  4. Rely on your backend's native durability (e.g. MongoDB/Redis stores persist implicitly) instead of persist().

Example fix

# before
kvstore: MutableMappingKVStore = CustomKVStore()
kvstore.persist("out.json")  # NotImplementedError

# after
import json
class CustomKVStore(MutableMappingKVStore):
    def persist(self, persist_path, fs=None):
        with open(persist_path, "w") as f:
            json.dump(self._data, f)
Defensive patterns

Strategy: type-guard

Validate before calling

if not hasattr(type(kvstore), 'persist') or type(kvstore).persist is MutableMappingKVStore.persist:
    raise TypeError('store cannot persist; use SimpleKVStore')
kvstore.persist(path)

Type guard

from llama_index.core.storage.kvstore.simple_kvstore import SimpleKVStore
from llama_index.core.storage.kvstore.types import MutableMappingKVStore

def is_persistable(store) -> bool:
    return not (type(store).persist is MutableMappingKVStore.persist)

Try / catch

try:
    kvstore.persist('out.json')
except NotImplementedError:
    # export via store-native mechanism instead
    ...

Prevention

When it happens

Trigger: Calling kvstore.persist('store.json') on an instance of MutableMappingKVStore or a subclass that does not override persist (e.g. a custom in-memory store); generic code that receives a MutableMappingKVStore-typed value and assumes persistence.

Common situations: Pluggable storage configurations where a custom or third-party store replaces SimpleKVStore but persistence code paths (StorageContext.persist, docstore.persist) were not updated; type annotations widened to MutableMappingKVStore letting non-persistable stores flow into persist calls.

Related errors


AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15). Data as JSON: /api/errors/6c48c86591ccdd1a. Report an issue: GitHub.