santifer/career-ops · error · Error

justjoin: careers_url or api must be a trusted justjoin.it U

Error message

justjoin: careers_url or api must be a trusted justjoin.it URL

What it means

Thrown by justjoin.fetch() at the very start of the fetch when detectUrl(entry) returns null — meaning the entry's api/careers_url is missing, empty, or failed every assertJustJoinUrl guard (invalid URL, non-HTTPS, wrong host, or wrong path). detect() uses the same detectUrl and would have returned null too, so reaching fetch() with a null detection implies the entry was forced into the justjoin provider without a valid justjoin.it URL.

Source

Thrown at providers/justjoin.mjs:110

        url: `${JOB_BASE}${slug}`,
        company: String(offer.companyName || '').trim(),
        location: normalizeLocation(offer),
        postedAt: postedAtMillis(offer.publishedAt),
      };
    })
    .filter(Boolean);
}

/** @type {Provider} */
export default {
  id: 'justjoin',

  detect(entry) {
    return detectUrl(entry);
  },

  async fetch(entry, ctx) {
    if (!detectUrl(entry)) throw new Error('justjoin: careers_url or api must be a trusted justjoin.it URL');

    const jobs = [];
    let from = 0;
    const maxPages = Number(entry.max_pages || MAX_PAGES);
    for (let page = 0; page < maxPages; page++) {
      const url = buildApiUrl(entry, from);
      const json = await ctx.fetchJson(url, { redirect: 'error' });
      const parsed = parseJustJoinResponse(json);
      jobs.push(...parsed);

      const next = json?.meta?.next?.cursor;
      if (next == null || parsed.length === 0) break;
      from = Number(next);
      if (!Number.isFinite(from)) break;
    }
    return jobs;
  },
};

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Set api to https://justjoin.it/api/candidate-api/offers or careers_url to a https://justjoin.it/job-offers/... URL.
  2. If the company is not on justjoin.it, change provider to the correct ATS.
  3. Remove the provider: justjoin assignment if the entry has no justjoin.it URL.

Example fix

# before — provider set but no valid URL
acme:
  provider: justjoin
  name: Acme

# after — provide the trusted URL
acme:
  provider: justjoin
  name: Acme
  api: https://justjoin.it/api/candidate-api/offers
Defensive patterns

Strategy: validation

Validate before calling

import { default as justjoin } from './providers/justjoin.mjs';
// Use the provider's own detect as the source of truth:
for (const e of entries) {
  if (e.provider === 'justjoin' && !justjoin.detect(e)) {
    console.warn(`justjoin ${e.name}: no valid justjoin.it URL — set api or careers_url`);
  }
}

Type guard

/** @param {any} entry @returns {boolean} */
function justjoinEntryHasValidUrl(entry) {
  const url = entry?.api || entry?.careers_url;
  if (typeof url !== 'string' || !url.trim()) return false;
  try {
    const p = new URL(url);
    return p.protocol === 'https:' && p.hostname === 'justjoin.it'
      && (p.pathname.startsWith('/job-offers') || p.pathname === '/api/candidate-api/offers');
  } catch { return false; }
}

Prevention

When it happens

Trigger: A portals.yml entry with provider: justjoin but no api and no careers_url; an api/careers_url that is a valid URL but not on justjoin.it (so detectUrl swallows the assertJustJoinUrl error and returns null); a non-string or whitespace-only url field.

Common situations: Forcing provider: justjoin on an entry that has no justjoin.it URL; an entry template where the url field was never filled in; an entry that points at a different board but was mislabeled justjoin.

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/26ae7be53d88c7eb. Report an issue: GitHub.