NousResearch/hermes-agent · error · Error
composer not found (${SELECTORS.composer}); is a chat view o
Error message
composer not found (${SELECTORS.composer}); is a chat view open? What it means
This error comes from the desktop app's keystroke perf scenario. It evaluates an INSTALL snippet in the page via CDP; the snippet locates the chat composer element using SELECTORS.composer and returns a truthy handle. A falsy result means the composer element was not found under the expected selector — typically because no chat view is open in the app window at scenario start.
Source
Thrown at apps/desktop/scripts/perf/scenarios/keystroke.mjs:60
`
const SENTENCE =
'the quick brown fox jumps over the lazy dog while typing into this composer, which should feel instant. '
export default {
name: 'keystroke',
tier: 'ci',
description: 'Composer keystroke → paint latency while idle.',
async run(cdp, opts = {}) {
const chars = Number(opts.chars ?? 120)
const cps = Number(opts.cps ?? 15)
await cdp.send('Runtime.enable')
const installed = await cdp.eval(INSTALL)
if (!installed) {
throw new Error(`composer not found (${SELECTORS.composer}); is a chat view open?`)
}
let text = ''
while (text.length < chars) {
text += SENTENCE
}
text = text.slice(0, chars)
const intervalMs = Math.max(1, Math.round(1000 / cps))
const start = Date.now()
for (let i = 0; i < text.length; i++) {
await cdp.eval('window.__KEY__.pending = performance.now()')
await cdp.send('Input.dispatchKeyEvent', { type: 'char', text: text[i], unmodifiedText: text[i] })
const wait = start + (i + 1) * intervalMs - Date.now()
if (wait > 0) {View on GitHub (pinned to c896c09c42)
Solutions
- Ensure the app is on an active chat view before the scenario runs (open/restore a session in the harness setup).
- Update SELECTORS.composer in the perf scripts to match the current composer DOM after a UI refactor.
- Add a wait/retry in the harness until the composer selector appears before running INSTALL.
Example fix
// before
await cdp.send('Runtime.enable')
const installed = await cdp.eval(INSTALL)
// after
await cdp.send('Runtime.enable')
await waitForSelector(cdp, SELECTORS.composer, { timeoutMs: 10_000 })
const installed = await cdp.eval(INSTALL) Defensive patterns
Strategy: retry
Validate before calling
async function waitForComposer(cdp: Cdp, selector: string, timeoutMs = 10_000): Promise<void> {
const deadline = Date.now() + timeoutMs
while (Date.now() < deadline) {
if (await cdp.eval(`!!document.querySelector(${JSON.stringify(selector)})`)) return
await new Promise(r => setTimeout(r, 250))
}
throw new Error(`composer (${selector}) never appeared — is a chat view open?`)
} Try / catch
try {
await run(cdp, opts)
} catch (err) {
if (String(err).includes('composer not found')) {
await navigateToChat(cdp) // click through to a chat view, then re-run
return run(cdp, opts)
}
throw err
} Prevention
- Make harness setup navigate to a chat session before running keystroke scenarios.
- Keep SELECTORS in one shared module updated with UI refactors (and covered by a DOM-contract test).
- Wait for the selector to appear instead of assuming mount completed.
When it happens
Trigger: Running the perf harness (apps/desktop/scripts/perf) against an app state where the composer is unmounted: the window sits on a non-chat route (settings, session picker, onboarding), or a selector/UI refactor changed the composer's DOM so SELECTORS.composer no longer matches.
Common situations: CI runs that launch the desktop app fresh without navigating to a chat; refactors that rename composer classes/attributes without updating SELECTORS; timing — scenario runs before React mounts the chat view.
Related errors
- gh pr create failed (is gh installed and authenticated?)
- Branch name is required.
- Secure token storage is unavailable (no OS keyring service w
- Failed to encrypt the remote gateway token for secure storag
- An update is already in progress.
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/81050317a959fe68.
Report an issue: GitHub.