Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Tool argument errors put the argument values in the log message body, outside `lk.pii.*`

オープン
#7,801 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@Li-john1021 がすでに取り組んでいます。

2026年10月14日 から。

  • #7804 @Li-john1021 による — オープン

評価

難易度
3/5
見積もり時間
半日
初心者へのやさしさ
25/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
python
領域
observability

調査の方向性

The message comes from prepare_function_arguments in livekit/agents/llm/utils.py, and the warning is logged in perform_tool_executions in voice/generation.py. Read telemetry/traces.py to see how lk.pii. fields are handled. Done means the ToolError warning logs a static message with the error text under a lk.pii. key. Pull request #7804 is already open against this issue, so check it before starting.

索引モデルが issue の本文から書いたものです。

説明

Bug Description

When a tool call's arguments can't be prepared, prepare_function_arguments raises a ToolError whose message includes the arguments:

  • a validation error (valid JSON, wrong type) embeds the pydantic error text, which includes input_value=... (code)
  • JSON that json_repair can't recover embeds the first 200 characters of the raw arguments (code, re-raised at L687)

perform_tool_executions then logs that message in the body of ToolError while executing tool: %s (code).

The same failure is also logged as error parsing arguments for ... with the arguments in lk.pii.arguments, so the value is marked there. The ToolError warning has no lk.pii. field, so the value is in the body, where it can't be dropped ("a log body cannot be redacted", traces.py). This is the same class as #7156 (participant identity in log bodies), at a call site that PR doesn't touch.

Expected Behavior

The argument values are not in the log body. For example, the warning logs a static message and puts the error text under a lk.pii. key, as error parsing arguments for ... already does. The ToolError returned to the model can keep the detail.

Reproduction Steps

Only livekit-agents, no audio or network. A scripted LLM returns one tool call, then a text reply, and the logs go through the CLI's JsonFormatter.

SECRET = "[email protected]"

@function_tool
async def lookup_orders(count: int) -> str:
    """Look up the customer's latest orders."""
    return "ok"

# scripted LLM: first chat() returns
#   FunctionToolCall(name="lookup_orders", arguments=json.dumps({"count": SECRET}), call_id="call_1")
# then a plain text reply
session = AgentSession(llm=ScriptedLLM())
await session.start(agent=Agent(instructions="test", tools=[lookup_orders]))
await session.run(user_input="show my orders")

Output (full script below):

== valid JSON, wrong type: '{"count": "[email protected]"}'
  [ERROR] 'error parsing arguments for `lookup_orders`'
      value in lk.pii.* fields: ['lk.pii.arguments'] | value in message body: False
  [WARNING] 'ToolError while executing tool: Error parsing arguments for `lookup_or'
      value in lk.pii.* fields: no | value in message body: True
== truncated JSON: '{"count": "[email protected]'
  [WARNING] 'repaired malformed function-call JSON arguments'
      value in lk.pii.* fields: ['lk.pii.raw_arguments', 'lk.pii.repaired'] | value in message body: False
  [ERROR] 'error parsing arguments for `lookup_orders`'
      value in lk.pii.* fields: ['lk.pii.arguments'] | value in message body: False
  [WARNING] 'ToolError while executing tool: Error parsing arguments for `lookup_or'
      value in lk.pii.* fields: no | value in message body: True

In the second case the SDK repairs the JSON, then validation fails the same way.

Full script
"""Minimal repro: a tool argument error puts the argument value in the log message body.

The SDK marks content-bearing log fields with an `lk.pii.` prefix so that redaction can drop them,
because a log body cannot be redacted. When a tool call's arguments fail validation (a string for an
`int` parameter), the pydantic error text, including `input_value=...`, goes into the ToolError
message. When the arguments are not valid JSON, the first 200 characters of the raw arguments go into
it. `voice/generation.py` then logs that message in the body of `ToolError while executing tool: %s`,
with no `lk.pii.` field, so dropping `lk.pii.*` does not remove the value.

Only livekit-agents is used (no audio, no network). A scripted LLM returns one tool call, then a plain
text reply. Logs are written with the CLI's JsonFormatter.

    pip install "livekit-agents==1.8.6"
    python repro.py
"""
import asyncio
import io
import json
import logging

from livekit.agents import Agent, AgentSession, function_tool, llm
from livekit.agents.cli.log import JsonFormatter
from livekit.agents.types import DEFAULT_API_CONNECT_OPTIONS

SECRET = "[email protected]"  # stands in for user data the model put in the arguments

CASES = {
    "valid JSON, wrong type": json.dumps({"count": SECRET}),
    "truncated JSON": '{"count": "' + SECRET,
}


class ScriptedLLM(llm.LLM):
    def __init__(self, arguments: str) -> None:
        super().__init__()
        self.calls = 0
        self.arguments = arguments

    def chat(self, *, chat_ctx, tools=None, conn_options=DEFAULT_API_CONNECT_OPTIONS, **kwargs):
        self.calls += 1
        return ScriptedStream(
            self, chat_ctx=chat_ctx, tools=tools or [], conn_options=conn_options, turn=self.calls
        )


class ScriptedStream(llm.LLMStream):
    def __init__(self, llm_, *, turn, **kwargs) -> None:
        super().__init__(llm_, **kwargs)
        self._turn = turn

    async def _run(self) -> None:
        if self._turn == 1:
            call = llm.FunctionToolCall(name="lookup_orders", arguments=self._llm.arguments, call_id="call_1")
            delta = llm.ChoiceDelta(role="assistant", tool_calls=[call])
        else:
            delta = llm.ChoiceDelta(role="assistant", content="Sorry, could you repeat that?")
        self._event_ch.send_nowait(llm.ChatChunk(id=f"chunk_{self._turn}", delta=delta))


@function_tool
async def lookup_orders(count: int) -> str:
    """Look up the customer's latest orders.

    Args:
        count: how many orders to return
    """
    return "ok"


async def run_case(name: str, arguments: str) -> None:
    print(f"== {name}: {arguments!r}")
    out = io.StringIO()
    handler = logging.StreamHandler(out)
    handler.setFormatter(JsonFormatter())
    root = logging.getLogger()
    root.handlers = [handler]
    root.setLevel(logging.INFO)

    session = AgentSession(llm=ScriptedLLM(arguments))
    await session.start(agent=Agent(instructions="test", tools=[lookup_orders]))
    await session.run(user_input="show my orders")
    await session.aclose()

    lines = [json.loads(line) for line in out.getvalue().splitlines()]
    for line in lines:
        if SECRET not in json.dumps(line):
            continue
        marked = [k for k, v in line.items() if k.startswith("lk.pii.") and SECRET in json.dumps(v)]
        in_body = SECRET in line.get("message", "")
        print(f"  [{line['level']}] {line['message'][:70]!r}")
        print(f"      value in lk.pii.* fields: {marked or 'no'} | value in message body: {in_body}")
    leaked = [line for line in lines if SECRET in line.get("message", "")]
    print(f"  records with the value in the message body: {len(leaked)}")


async def main() -> None:
    for name, arguments in CASES.items():
        await run_case(name, arguments)


asyncio.run(main())
Operating System

Windows 11

Models Used

None (scripted LLM). In a real call this needs the model to send arguments that fail validation, for example a customer's email or phone number in a field of another type.

Package Versions
livekit-agents==1.8.6
python==3.13

The same code is on main and in [email protected].

Additional Context

We drop lk.pii.* from our stdout logs before they reach CloudWatch. In the tool path, this warning is where we found the value still getting through.

主要言語
Python
スター
14.6k
フォーク
3.9k
平均マージ
3日 3時間
マージ済み PR(30日)
171

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

livekit/agents のほかの issue

livekit/agents の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。