-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
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