Skip to content

Tool handle() receives named args causing 'Unknown named parameter' when using Laravel AI SDK's ->using() closure #956

@h-shvedko

Description

@h-shvedko

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 PrismException handler in executeToolCall(), making it invisible
  • The LLM sees "Parameter validation error" and retries until maxSteps is exhausted
  • Tools that work around this (e.g., using named params matching the tool schema) are unaffected

Affected Code

  • src/Concerns/CallsTools.php:134-136call_user_func_array with named args
  • src/Tool.php:264call_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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions