langchain-ai/langchain · error · ValueError

'token_counter' expected to be a model that implements 'get_

Error message

'token_counter' expected to be a model that implements 'get_num_tokens_from_messages()' or a function. Received object of type {type(actual_token_counter)}.

What it means

Raised by `trim_messages` when `token_counter` is neither a string shortcut, an object exposing `get_num_tokens_from_messages`, nor a callable. The function must be able to derive a per-message/per-list token count, so opaque objects of any other type are rejected.

Source

Thrown at libs/core/langchain_core/messages/utils.py:1489

        if (
            next(
                iter(inspect.signature(actual_token_counter).parameters.values())
            ).annotation
            is BaseMessage
        ):

            def list_token_counter(messages: Sequence[BaseMessage]) -> int:
                return sum(actual_token_counter(msg) for msg in messages)  # type: ignore[arg-type, misc]

        else:
            list_token_counter = actual_token_counter
    else:
        msg = (  # type: ignore[unreachable]
            f"'token_counter' expected to be a model that implements "
            f"'get_num_tokens_from_messages()' or a function. Received object of type "
            f"{type(actual_token_counter)}."
        )
        raise ValueError(msg)

    text_splitter_fn: Callable[[str], list[str]]
    if _HAS_LANGCHAIN_TEXT_SPLITTERS and isinstance(text_splitter, TextSplitter):
        text_splitter_fn = text_splitter.split_text
    elif text_splitter:
        text_splitter_fn = cast("Callable[[str], list[str]]", text_splitter)
    else:
        text_splitter_fn = _default_text_splitter

    if strategy == "first":
        return _first_max_tokens(
            messages,
            max_tokens=max_tokens,
            token_counter=list_token_counter,
            text_splitter=text_splitter_fn,
            partial_strategy="first" if allow_partial else None,
            end_on=end_on,
        )

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Pass a real language-model instance (it implements `get_num_tokens_from_messages`)
  2. Or pass a plain callable such as `len`, a tokenizer wrapper, or `lambda msgs: sum(count_tokens(m.content) for m in msgs)`
  3. If the counter is optional, only pass it when it is not None

Example fix

# before
trim_messages(msgs, max_tokens=500, token_counter={'model': 'gpt-4o'})

# after
trim_messages(msgs, max_tokens=500, token_counter=len)
Defensive patterns

Strategy: type-guard

Validate before calling

def usable_token_counter(tc) -> bool:
    return callable(tc) or hasattr(tc, 'get_num_tokens_from_messages')

assert usable_token_counter(token_counter), 'token_counter must be a model instance or callable'

Type guard

def is_token_counter(tc: object) -> bool:
    return callable(tc) or hasattr(tc, 'get_num_tokens_from_messages')

Prevention

When it happens

Trigger: Passing an uninitialized model class (not an instance), a tokenizer config dict, a string name object, or some other non-callable as `token_counter`.

Common situations: Passing the class instead of the instance (`token_counter=BaseLanguageModel` subclass); passing a serialized model config; a variable that is None after a failed initialization.

Related errors


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/8d4c2b7fa9e5ec60. Report an issue: GitHub.