{"record":{"id":"81050317a959fe68","repo":"NousResearch/hermes-agent","slug":"composer-not-found-selectors-composer-is-a-c","errorCode":null,"errorMessage":"composer not found (${SELECTORS.composer}); is a chat view open?","messagePattern":"composer not found \\((.+?)\\); is a chat view open\\?","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/desktop/scripts/perf/scenarios/keystroke.mjs","lineNumber":60,"sourceCode":"`\n\nconst SENTENCE =\n  'the quick brown fox jumps over the lazy dog while typing into this composer, which should feel instant. '\n\nexport default {\n  name: 'keystroke',\n  tier: 'ci',\n  description: 'Composer keystroke → paint latency while idle.',\n  async run(cdp, opts = {}) {\n    const chars = Number(opts.chars ?? 120)\n    const cps = Number(opts.cps ?? 15)\n\n    await cdp.send('Runtime.enable')\n\n    const installed = await cdp.eval(INSTALL)\n\n    if (!installed) {\n      throw new Error(`composer not found (${SELECTORS.composer}); is a chat view open?`)\n    }\n\n    let text = ''\n\n    while (text.length < chars) {\n      text += SENTENCE\n    }\n\n    text = text.slice(0, chars)\n    const intervalMs = Math.max(1, Math.round(1000 / cps))\n    const start = Date.now()\n\n    for (let i = 0; i < text.length; i++) {\n      await cdp.eval('window.__KEY__.pending = performance.now()')\n      await cdp.send('Input.dispatchKeyEvent', { type: 'char', text: text[i], unmodifiedText: text[i] })\n      const wait = start + (i + 1) * intervalMs - Date.now()\n\n      if (wait > 0) {","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/scripts/perf/scenarios/keystroke.mjs#L42-L78","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait cdp.send('Runtime.enable')\nconst installed = await cdp.eval(INSTALL)\n\n// after\nawait cdp.send('Runtime.enable')\nawait waitForSelector(cdp, SELECTORS.composer, { timeoutMs: 10_000 })\nconst installed = await cdp.eval(INSTALL)","handlingStrategy":"retry","validationCode":"async function waitForComposer(cdp: Cdp, selector: string, timeoutMs = 10_000): Promise<void> {\n  const deadline = Date.now() + timeoutMs\n  while (Date.now() < deadline) {\n    if (await cdp.eval(`!!document.querySelector(${JSON.stringify(selector)})`)) return\n    await new Promise(r => setTimeout(r, 250))\n  }\n  throw new Error(`composer (${selector}) never appeared — is a chat view open?`)\n}","typeGuard":null,"tryCatchPattern":"try {\n  await run(cdp, opts)\n} catch (err) {\n  if (String(err).includes('composer not found')) {\n    await navigateToChat(cdp) // click through to a chat view, then re-run\n    return run(cdp, opts)\n  }\n  throw err\n}","preventionTips":["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."],"tags":["perf","e2e","desktop","selectors"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}