-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Open
Description
Before submitting an issue, please:
- Check the documentation: https://docs.stagehand.dev/
- Search existing issues: https://github.com/browserbase/stagehand/issues
Environment Information
Stagehand:
- Language/SDK: TypeScript
- Stagehand version: 3.0.1
- Chrome: 144
Issue Description
Locator.fill() (and act({ method: 'fill' })) on native HTML5 date/time inputs resolve without error, but an immediate locator.inputValue() reads back an empty string. Setting the value via the native setter inside the page context (evaluate) and dispatching input/change events succeeds consistently.
Steps to Reproduce
- Initialize Stagehand locally (no LLM needed).
- Navigate to a data: URL containing a native
<input type="date">(and optionally<input type="time">). - Call
page.locator(xpath).fill('2025-11-15'); immediately calllocator.inputValue()— observe empty string. - Call
stagehand.act({ selector, method: 'fill', arguments: [...] }); immediately calllocator.inputValue()— observe empty string. - Call
page.evaluate()to set via native setter and dispatch input/change; immediately calllocator.inputValue()— observe the expected value.
Minimal Reproduction Code
import { Stagehand } from "@browserbasehq/stagehand";
function htmlDemo() {
return `<!doctype html>
<meta charset="utf-8" />
<title>Stagehand date/time fill demo</title>
<style>
body { font-family: system-ui, sans-serif; margin: 24px; }
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.card { border: 1px solid #ddd; border-radius: 8px; padding: 16px; }
.card h2 { margin: 0 0 8px; font-size: 16px; }
.row { margin: 8px 0; display: flex; gap: 8px; align-items: center; }
label { display: inline-flex; align-items: center; gap: 6px; }
input { padding: 6px 8px; border: 1px solid #ccc; border-radius: 6px; }
.status { margin-top: 8px; font-size: 12px; color: #555; }
.ok input { border-color: #16a34a; box-shadow: 0 0 0 2px #16a34a22; }
.ok .status { color: #16a34a; }
.fail input { border-color: #dc2626; box-shadow: 0 0 0 2px #dc262622; }
.fail .status { color: #dc2626; }
.hint { color: #666; font-size: 12px; margin-bottom: 8px; }
</style>
<h1>Stagehand native date/time fill — broken vs working</h1>
<p class="hint">Demonstrates two broken approaches (locator.fill, act(fill)) and one working approach (evaluate with native setter).</p>
<div class="grid">
<div class="card" id="card-loc">
<h2>Locator.fill()</h2>
<div class="row"><label>Date <input id="d_loc" type="date"></label></div>
<div class="row"><label>Time <input id="t_loc" type="time"></label></div>
<div class="status" id="status_loc">Pending…</div>
</div>
<div class="card" id="card-act">
<h2>act({ method: 'fill' })</h2>
<div class="row"><label>Date <input id="d_act" type="date"></label></div>
<div class="row"><label>Time <input id="t_act" type="time"></label></div>
<div class="status" id="status_act">Pending…</div>
</div>
<div class="card" id="card-eval">
<h2>Evaluate (native setter)</h2>
<div class="row"><label>Date <input id="d_eval" type="date"></label></div>
<div class="row"><label>Time <input id="t_eval" type="time"></label></div>
<div class="status" id="status_eval">Pending…</div>
</div>
</div>`;
}
async function setViaEvaluate(page: any, selector: string, val: string) {
await page.evaluate(({ xp, val }) => {
const doc = (globalThis as any).document;
const xpExpr = xp.startsWith("xpath=") ? xp.slice(6) : xp;
const res = doc.evaluate(xpExpr, doc, null, 9, null);
const el = res.singleNodeValue as any;
if (!el || String(el.tagName || "").toUpperCase() !== "INPUT") return;
const desc = (globalThis as any).HTMLInputElement?.prototype &&
Object.getOwnPropertyDescriptor((globalThis as any).HTMLInputElement.prototype, "value");
if (desc && typeof desc.set === "function") desc.set.call(el, val);
else el.value = String(val);
const E = (globalThis as any).Event;
el.dispatchEvent(new E("input", { bubbles: true }));
el.dispatchEvent(new E("change", { bubbles: true }));
}, { xp: selector, val });
}
async function markCard(page: any, cardId: string, dateId: string, timeId: string, readDate: string, readTime: string, expectDate: string, expectTime: string) {
await page.evaluate(({ cardId, dateId, timeId, readDate, readTime, expectDate, expectTime }) => {
const doc = (globalThis as any).document;
const card = doc.getElementById(cardId);
const d = doc.getElementById(dateId) as any;
const t = doc.getElementById(timeId) as any;
const status = doc.getElementById('status\_' + cardId.split('-')[1]);
const ok = readDate === expectDate && readTime === expectTime;
if (card) card.classList.remove('ok','fail');
if (card) card.classList.add(ok ? 'ok' : 'fail');
if (status) status.textContent = `Date=${JSON.stringify(readDate)} Time=${JSON.stringify(readTime)} ${ok ? '✓ OK' : '✗ FAIL'}`;
// Also reflect final values in the inputs for visibility
if (d) d.value = readDate;
if (t) t.value = readTime;
}, { cardId, dateId, timeId, readDate, readTime, expectDate, expectTime });
}
async function main() {
const sh = new Stagehand({ env: "LOCAL", verbose: 1, localBrowserLaunchOptions: { headless: false } });
await sh.init();
const page = sh.context.pages()[0];
if (!page) throw new Error("no page");
await page.goto(`data:text/html,${encodeURIComponent(htmlDemo())}`);
await page.waitForLoadState("domcontentloaded");
const date = "2025-11-15";
const time = "10:00";
// Locator.fill() card
await page.locator('xpath=//_[@id="d_loc"]').fill(date);
await page.locator('xpath=//_[@id="t_loc"]').fill(time);
const dLoc = await page.locator('xpath=//_[@id="d_loc"]').inputValue();
const tLoc = await page.locator('xpath=//_[@id="t_loc"]').inputValue();
await markCard(page, 'card-loc', 'd_loc', 't_loc', dLoc, tLoc, date, time);
// act({ method: 'fill' }) card
await sh.act({ selector: 'xpath=//_[@id="d_act"]', description: 'date via act', method: 'fill', arguments: [date] });
await sh.act({ selector: 'xpath=//_[@id="t_act"]', description: 'time via act', method: 'fill', arguments: [time] });
const dAct = await page.locator('xpath=//_[@id="d_act"]').inputValue();
const tAct = await page.locator('xpath=//_[@id="t_act"]').inputValue();
await markCard(page, 'card-act', 'd_act', 't_act', dAct, tAct, date, time);
// Evaluate (native setter) card
await setViaEvaluate(page, 'xpath=//_[@id="d_eval"]', date);
await setViaEvaluate(page, 'xpath=//_[@id="t_eval"]', time);
const dEval = await page.locator('xpath=//_[@id="d_eval"]').inputValue();
const tEval = await page.locator('xpath=//_[@id="t_eval"]').inputValue();
await markCard(page, 'card-eval', 'd_eval', 't_eval', dEval, tEval, date, time);
console.log("\nOpen browser to see three cards: two broken (red), one working (green). Close tab to finish.");
}
main().catch((err) => { console.error(err); process.exit(1); });Error Messages / Log trace
============================================================
BROKEN PATHS (expected to be flaky or empty)
============================================================
Date: locator.fill → inputValue (expected empty)
[2025-11-08T06:25:12.978Z] DEBUG: xpath main-world
frameId: 55A9F6AEBE837C4D00A34C645D4510D3
xp: //*[@id="d"]
ctxId: 2
[2025-11-08T06:25:12.984Z] DEBUG: xpath main-world
frameId: 55A9F6AEBE837C4D00A34C645D4510D3
xp: //*[@id="d"]
ctxId: 2
[2025-11-08T06:25:12.998Z] DEBUG: xpath main-world
frameId: 55A9F6AEBE837C4D00A34C645D4510D3
xp: //*[@id="d"]
ctxId: 2
after fill: ""
Date: act(fill) → inputValue (expected empty)
[2025-11-08T06:25:13.025Z] DEBUG: xpath main-world
frameId: 55A9F6AEBE837C4D00A34C645D4510D3
xp: //*[@id="d"]
ctxId: 2
after act(fill): ""
Time: locator.fill → inputValue (expected empty)
[2025-11-08T06:25:13.028Z] DEBUG: xpath main-world
frameId: 55A9F6AEBE837C4D00A34C645D4510D3
xp: //*[@id="t"]
ctxId: 2
[2025-11-08T06:25:13.031Z] DEBUG: xpath main-world
frameId: 55A9F6AEBE837C4D00A34C645D4510D3
xp: //*[@id="t"]
ctxId: 2
[2025-11-08T06:25:13.035Z] DEBUG: xpath main-world
frameId: 55A9F6AEBE837C4D00A34C645D4510D3
xp: //*[@id="t"]
ctxId: 2
after fill: ""
Time: act(fill) → inputValue (expected empty)
[2025-11-08T06:25:13.058Z] DEBUG: xpath main-world
frameId: 55A9F6AEBE837C4D00A34C645D4510D3
xp: //*[@id="t"]
ctxId: 2
after act(fill): ""
============================================================
WORKING PATHS (expected to succeed)
============================================================
Date: evaluate(native setter) → inputValue (expected "2025-11-15")
[2025-11-08T06:25:13.063Z] DEBUG: xpath main-world
frameId: 55A9F6AEBE837C4D00A34C645D4510D3
xp: //*[@id="d"]
ctxId: 2
after evaluate: "2025-11-15"
Time: evaluate(native setter) → inputValue (expected "10:00")
[2025-11-08T06:25:13.068Z] DEBUG: xpath main-world
frameId: 55A9F6AEBE837C4D00A34C645D4510D3
xp: //*[@id="t"]
ctxId: 2
after evaluate: "10:00"
Done. Close the browser to finish.
Metadata
Metadata
Assignees
Labels
No labels