Skip to content

fillForm agent tool drops value parameter, causing LLM to hallucinate fill values #1789

@tiwalayo

Description

@tiwalayo

Summary

The fillForm agent tool receives a value parameter but never uses it. When the tool calls observe(), it only passes the action description, causing the LLM to hallucinate placeholder values like test@example.com instead of using the actual provided value. This bug also affects custom tool results (e.g., TOTP codes from 2FA tools).

Affected File

packages/core/lib/v3/agent/tools/fillform.ts

Reproduction

Using the agent to fill a login form:

const agent = stagehand.agent({
  model: { modelName: "openai/gpt-5.1-2025-11-13" },
  systemPrompt: "Log into the portal",
});

await agent.execute({
  instruction: `Enter "tiwa@trysplendor.com" in the email field and "mypassword" in the password field`,
  page,
});

The agent correctly parses the instruction and calls fillForm with the right values:

Agent calling tool: fillForm
arguments: [{"action": "type email into the One Healthcare ID or Email Address textbox", "value": "tiwa@trysplendor.com"}]

But the observe() response shows a hallucinated placeholder:

{
  "elements": [{
    "elementId": "0-395",
    "description": "Textbox labeled 'One Healthcare ID or Email Address'",
    "method": "fill",
    "arguments": ["test@example.com"]
  }]
}

The wrong value test@example.com is then typed into the field.

Root Cause

The tool receives fields with both action and value:

inputSchema: z.object({
  fields: z.array(z.object({
    action: z.string().describe('Description of typing action'),
    value: z.string().describe("Text to type into the target")  // <-- This exists!
  }))
})

But the execute function only uses action:

execute: async ({ fields }) => {
  // BUG: Only uses action, completely ignores value
  const instruction = `Return observation results for the following actions: ${fields
    .map((f) => f.action)
    .join(", ")}`;
  // ...
}

Flaky Behavior

This manifests as flaky behavior because it depends on how the agent formulates the action string:

  • Works when agent embeds value in action: {"action": "type tiwa@example.com into email", "value": "tiwa@example.com"}
  • Fails when agent separates them: {"action": "type into email field", "value": "tiwa@example.com"}

Additional Impact: Custom Tool Results Are Also Dropped

This affects values returned by custom tools. For example, when using a TOTP tool for 2FA, the agent correctly calls fillForm with { action: "type code into verification field", value: "123456" }, but the value is dropped and the LLM hallucinates a placeholder, breaking 2FA flows entirely.

Suggested Fix

Override the arguments in observe results with the original value before calling act():

for (let i = 0; i < observeResults.length; i++) {
  const res = observeResults[i];

  // FIX: Inject the actual value from fields into the observe result
  if (res.method === "fill" && fields[i]?.value) {
    res.arguments = [fields[i].value];
  }

  const actResult = await v3.act(res, actOptions);
  // ...
}

Environment

  • Stagehand version: 3.0.6 (also verified bug exists in 3.1.0 source)
  • Using stagehand.agent() API

Impact

This bug makes the fillForm tool effectively broken for any use case where specific values matter:

  • Login forms
  • Search queries
  • Data entry
  • 2FA/OTP flows
  • Any workflow using custom tool results

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