langchain-ai/deepagents · error · ValueError

PTC tool name {tool.name!r} cannot be exposed as JavaScript

Error message

PTC tool name {tool.name!r} cannot be exposed as JavaScript identifier {camel!r}. Tool names must map to `/^[A-Za-z_$][A-Za-z0-9_$]*$/`.

What it means

Each PTC tool is exposed in JavaScript as a camelCase identifier; a tool whose name cannot be converted to a valid JS identifier (must match `/^[A-Za-z_$][A-Za-z0-9_$]*$/`) would generate broken/unreachable JS bindings, so it is rejected with ValueError during PTC tool validation or prompt rendering.

Source

Thrown at libs/partners/quickjs/langchain_quickjs/_ptc.py:144

    return _prompt.is_valid_js_identifier(name)


def is_valid_ptc_tool_name(name: str) -> bool:
    """Return whether a tool can be exposed as `tools.<camelCaseName>`."""
    return _prompt.is_valid_ptc_tool_name(name)


def _raise_on_invalid_ptc_tools(tools: Sequence[BaseTool]) -> None:
    for tool in tools:
        camel = to_camel_case(tool.name)
        if is_valid_js_identifier(camel):
            continue
        msg = (
            f"PTC tool name {tool.name!r} cannot be exposed as JavaScript "
            f"identifier {camel!r}. Tool names must map to "
            "`/^[A-Za-z_$][A-Za-z0-9_$]*$/`."
        )
        raise ValueError(msg)


def render_ptc_prompt(tools: Sequence[BaseTool], *, tool_name: str = "eval") -> str:
    """Build the `tools` namespace section of the system prompt."""
    if not tools:
        return ""
    _raise_on_invalid_ptc_tools(tools)
    return _prompt.render_ptc_prompt(tools, tool_name=tool_name)

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Rename the tool so it is alphanumeric snake_case starting with a letter (e.g. `web_search` instead of `/search/web`)
  2. Set an explicit JS-safe tool name via the tool's name/alias when registering
  3. Sanitize third-party tool names before passing them into a PTC-enabled agent

Example fix

# before
@tool
def 3d-render(query: str) -> str: ...
# after
@tool
def render_3d(query: str) -> str: ...
Defensive patterns

Strategy: validation

Validate before calling

import re
_JS_IDENT = re.compile(r"^[A-Za-z_$][A-Za-z0-9_$]*$")
for t in tools:
    camel = to_camel_case(t.name)
    if not _JS_IDENT.match(camel):
        raise ValueError(f"tool {t.name!r} -> {camel!r} is not a valid JS identifier")

Type guard

def is_js_safe_tool_name(name: str) -> bool:
    camel = to_camel_case(name)
    return bool(re.fullmatch(r"[A-Za-z_$][A-Za-z0-9_$]*", camel))

Try / catch

try:
    selected = filter_tools_for_ptc(tools, config)
except ValueError as e:
    if "JavaScript identifier" in str(e):
        tools = [rename_tool(t, sanitize_js_name(t.name)) for t in tools]
        selected = filter_tools_for_ptc(tools, config)
    else:
        raise

Prevention

When it happens

Trigger: Calling `filter_tools_for_ptc` or `render_ptc_prompt` with a tool whose name, after camelCase conversion, starts with a digit, contains characters like `-`, `.`, `/`, or unicode, or is empty.

Common situations: Tool names derived from HTTP routes or file paths (`/search/web`, `my-tool.v2`); names starting with digits (`3d_render`); dynamically generated tool names from user input.

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/56db0134ba9912a6. Report an issue: GitHub.