-
-
Notifications
You must be signed in to change notification settings - Fork 278
Description
Bug Description
When tools are executed via CallsTools::executeToolCall(), the tool arguments are passed as named parameters through call_user_func_array(), which then get spread to the ->using() closure as named args. Since the closure typically has a generic parameter name like $arguments, PHP throws Unknown named parameter $path (or whatever the tool's first param is).
The error is silently caught by PrismException in executeToolCall() (line 175) and converted to a tool error result. The LLM then sees a "Parameter validation error" and retries, creating an infinite retry loop until maxSteps is exhausted.
Reproduction
$tool = (new \Prism\Prism\Tool)
->as('write_file')
->for('Write a file')
->withStringParameter('path', 'File path')
->withStringParameter('content', 'File content')
->using(fn ($arguments) => 'wrote file: ' . json_encode($arguments))
->withoutErrorHandling();
// Simulating what CallsTools::executeToolCall does:
$toolCall = new \Prism\Prism\ValueObjects\ToolCall(
id: 'test',
name: 'write_file',
arguments: ['path' => 'test.txt', 'content' => 'hello world'],
);
// This throws "Unknown named parameter $path"
$output = call_user_func_array(
$tool->handle(...),
$toolCall->arguments() // Returns ['path' => '...', 'content' => '...']
);Root Cause
In src/Concerns/CallsTools.php line 134-136:
$output = call_user_func_array(
$tool->handle(...),
$toolCall->arguments()
);$toolCall->arguments() returns an associative array like ['path' => 'test.txt', 'content' => 'hello']. When passed through call_user_func_array, PHP treats these as named arguments to Tool::handle(...$args).
Inside Tool::handle() (line 264), call_user_func($this->fn, ...$args) then spreads these named args to the ->using() closure. Since the closure parameter is typically named $arguments (not $path), PHP throws Unknown named parameter $path.
Impact
- All tools with >1 parameter are affected when using the
->using()closure pattern - The error is silently caught by
PrismExceptionhandler inexecuteToolCall(), making it invisible - The LLM sees "Parameter validation error" and retries until
maxStepsis exhausted - Tools that work around this (e.g., using named params matching the tool schema) are unaffected
Affected Code
src/Concerns/CallsTools.php:134-136—call_user_func_arraywith named argssrc/Tool.php:264—call_user_func($this->fn, ...$args)spreading named args to closure
Suggested Fix
In CallsTools::executeToolCall(), wrap arguments in a positional array before calling:
// Before (broken):
$output = call_user_func_array(
$tool->handle(...),
$toolCall->arguments()
);
// After (fixed):
$output = $tool->handle($toolCall->arguments());Or in Tool::handle(), normalize $args to a single positional array:
public function handle(...$args): string|ToolOutput
{
try {
// Normalize: if named args were spread, recombine into single array
$arguments = count($args) === 1 && is_array($args[0]) ? $args[0] : $args;
$value = call_user_func($this->fn, $arguments);
// ...Environment
- prism-php/prism: v0.99.22
- laravel/ai: v0.2.8
- PHP: 8.4
- Provider: Anthropic (Claude)