jackwener/OpenCLI · warning · EmptyResultError

No Indeed job posting found for jk "${jk}"

Error message

No Indeed job posting found for jk "${jk}"

What it means

An EmptyResultError thrown when the job page loaded successfully but reports the posting as not found, or no title and no description could be extracted. It means the jk (job key) does not correspond to a live Indeed posting.

Source

Thrown at clis/indeed/job.js:72

                    .map(s => (s.textContent || '').trim())
                    .filter(t => t && t !== salary)
                    .join(', ');
                const description = document.querySelector('#jobDescriptionText')?.innerText?.trim() ?? '';
                return { ready, challenge, notFound, title, company, location, salary, jobType, description };
            })()`);
        }
        catch (e) {
            throw new CommandExecutionError(`Failed to scrape Indeed job detail DOM: ${e?.message ?? e}`, 'The page may not have fully loaded; try again.');
        }

        if (detail?.challenge) {
            throw new CommandExecutionError('Indeed served a Cloudflare challenge page', 'Open https://www.indeed.com in the connected browser and clear the challenge, then retry.');
        }
        if (!detail?.ready) {
            throw new CommandExecutionError('Indeed job page did not expose detail or error markers within 15s', 'Indeed may still be loading or the DOM shape may have changed; retry after opening Indeed in the connected browser.');
        }
        if (detail?.notFound || (!detail?.title && !detail?.description)) {
            throw new EmptyResultError('indeed job', `No Indeed job posting found for jk "${jk}"`);
        }

        return [{
            id: jk,
            title: detail.title.replace(/\s+/g, ' ').trim(),
            company: detail.company.replace(/\s+/g, ' ').trim(),
            location: detail.location.replace(/\s+/g, ' ').trim(),
            salary: detail.salary.replace(/\s+/g, ' ').trim(),
            job_type: detail.jobType.replace(/\s+/g, ' ').trim(),
            description: detail.description,
            url,
        }];
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify the jk value is correct and complete (32-char job key)
  2. Re-obtain the jk from a fresh Indeed search — the posting may have expired
  3. Open the job URL directly in the connected browser to confirm it is live
  4. Check the posting's region/locale matches your browser session
  5. If you scrape many postings, refresh your source list to drop dead postings
Defensive patterns

Strategy: fallback

Validate before calling

// refresh jk values from a recent search before fetching details
const fresh = await indeed.search({ query: title, location });
if (!fresh.some(j => j.id === jk)) throw new Error(`jk ${jk} may be expired`);

Try / catch

try {
  const job = await indeed.job(jk);
} catch (e) {
  if (e instanceof EmptyResultError || /No Indeed job posting/.test(e.message)) {
    // skip this posting / fall back to a fresh search
  }
  throw e;
}

Prevention

When it happens

Trigger: detail.notFound is true, or both detail.title and detail.description are empty after a ready job page scrape for the given jk.

Common situations: Job posting was taken down/expired since you got the jk; jk copied incorrectly or truncated; posting is region-locked and renders empty for your browser's locale; Indeed removed the listing without redirecting.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/de0b56f152b2c8a9. Report an issue: GitHub.