{"record":{"id":"d94c281eb10a3016","repo":"jackwener/OpenCLI","slug":"working-tree-not-clean-status","errorCode":null,"errorMessage":"Working tree not clean:\n${status}","messagePattern":"Working tree not clean:\n(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"autoresearch/engine.ts","lineNumber":94,"sourceCode":"  constructor(config: AutoResearchConfig, logPath: string, callbacks: EngineCallbacks) {\n    this.config = config;\n    this.logger = new Logger(logPath);\n    this.callbacks = callbacks;\n  }\n\n  private log(msg: string): void {\n    this.callbacks.onStatus?.(msg);\n  }\n\n  /** Phase 0: Precondition checks */\n  private checkPreconditions(): void {\n    // Git repo exists\n    try { execStrict('git rev-parse --git-dir'); }\n    catch { throw new Error('Not a git repository'); }\n\n    // Clean working tree\n    const status = exec('git status --porcelain');\n    if (status) throw new Error(`Working tree not clean:\\n${status}`);\n\n    // No stale locks\n    if (existsSync(join(ROOT, '.git', 'index.lock'))) {\n      throw new Error('Stale .git/index.lock found — remove it first');\n    }\n\n    // Not detached HEAD\n    try { execStrict('git symbolic-ref HEAD'); }\n    catch { throw new Error('Detached HEAD — checkout a branch first'); }\n  }\n\n  /** Phase 5: Run verify command and extract metric */\n  private runVerify(): number | null {\n    this.log('  verify...');\n    const output = exec(this.config.verify, { timeout: 300_000 });\n    return extractMetric(output);\n  }\n","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/autoresearch/engine.ts#L76-L112","documentation":"This EMPTY_RESULT error is thrown by extractEventRowsPayload when the Huodongxing events page contained zero event cards at all — the code path after the MALFORMED_RESULT branch. It means extraction found no candidate elements whatsoever, so the page was either genuinely empty or its DOM layout changed so much that no cards were recognized. Unlike MALFORMED_RESULT, no card-like markup was seen.","triggerScenarios":"extractEventRows runs against a fetched Huodongxing events page whose card selector matches 0 elements (cards.length === 0) and no earlier guard (busy page / malformed cards) fired.","commonSituations":"The events listing genuinely has no upcoming events; the request landed on an empty shell page because a bot wall blocked the content; the site moved the events list to a new container so the scraper's selector no longer matches anything; network fetch silently returned a near-empty page.","solutions":["Retry after a short wait — a temporary busy/interstitial page can look empty; use the same browser session/cookies.","Dump the fetched HTML and check whether real event cards exist under different selectors; update the card selector in extractEventRowsPayload if the DOM changed.","Verify the target URL and pagination parameters actually point at a populated events listing.","If the listing is legitimately empty, treat EMPTY_RESULT as an expected business outcome rather than a fault."],"exampleFix":"// before\nconst rows = extractFromPayload(html);\n// after\nif (!rows.length) {\n  const html = await page.content();\n  require('fs').writeFileSync('debug-events.html', html); // inspect and update selectors\n}","handlingStrategy":"retry","validationCode":"const html = await page.content();\nif (!html.includes('event') && html.length < 2000) {\n  console.warn('Events page looks empty/interstitial — likely busy page; retry before extraction');\n}","typeGuard":"function hasEventRows(payload: unknown): payload is { rows: unknown[] } {\n  return typeof payload === 'object' && payload !== null &&\n    Array.isArray((payload as any).rows) && (payload as any).rows.length > 0;\n}","tryCatchPattern":"try {\n  const res = await extractEventRows();\n} catch (err) {\n  if (err.code === 'EMPTY_RESULT') {\n    await sleep(RETRY_DELAY_MS);\n    return retryExtract(); // busy pages often look empty on first hit\n  }\n  throw err;\n}","preventionTips":["Retry once or twice with backoff before accepting an empty result — busy/interstitial pages often render zero cards.","Persist the fetched HTML whenever EMPTY_RESULT occurs so you can distinguish a truly empty listing from selector drift.","Monitor the events URL manually after any Huodongxing release window.","Treat legitimate empty listings (no upcoming events) as expected and alert only on sudden changes from a previously non-empty listing."],"tags":["scraping","empty-response","dom-parsing","cli"],"backgroundTag":"empty-scrape-result","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}