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

  1. Ensure the app is on an active chat view before the scenario runs (open/restore a session in the harness setup).
  2. Update SELECTORS.composer in the perf scripts to match the current composer DOM after a UI refactor.
  3. 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

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


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/81050317a959fe68. Report an issue: GitHub.