{"record":{"id":"080cbf5d00e33711","repo":"santifer/career-ops","slug":"glints-http-err-status-detail","errorCode":null,"errorMessage":"glints: HTTP ${err.status} — ${detail}","messagePattern":"glints: HTTP (.+?) — (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/glints.mjs","lineNumber":176,"sourceCode":"        'user-agent': BROWSER_LIKE_USER_AGENT,\n        'origin': 'https://glints.com',\n        'referer': 'https://glints.com/id/opportunities/jobs/explore',\n      },\n      body,\n      redirect: 'error',\n    });\n    return res;\n  } catch (err) {\n    // On POST, some servers return non-JSON errors; attempt text fallback\n    if (err.status && err.body) {\n      let detail = '';\n      try {\n        const parsed = JSON.parse(err.body);\n        detail = parsed.errors?.[0]?.message || err.body.slice(0, 200);\n      } catch {\n        detail = err.body.slice(0, 200);\n      }\n      throw new Error(`glints: HTTP ${err.status} — ${detail}`);\n    }\n    throw err;\n  }\n}\n\n/** @type {Provider} */\nexport default {\n  id: 'glints',\n\n  detect(_entry) {\n    // Glints is a job board aggregator, not a company ATS.\n    // Auto-detection is intentionally not supported —\n    // use `provider: glints` explicitly in portals.yml.\n    return null;\n  },\n\n  async fetch(entry, ctx) {\n    const apiUrl = entry.api || DEFAULT_API;","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/glints.mjs#L158-L194","documentation":"glints.mjs throws this in graphqlPage() when ctx.fetchJson rejects with an error that carries both .status and .body — i.e. a non-2xx HTTP response with a body. The handler tries to parse the body as JSON and extract errors[0].message, falling back to the first 200 chars of the raw body. So the thrown text identifies the HTTP status and the server's error detail, which for Glints is most often a WAF/firewall block, rate limiting, or a request the server rejected.","triggerScenarios":"Glints' firewall blocks the request because the User-Agent/origin/referer headers look non-browser (the code already sends a Chrome UA, so a block usually means the header set changed or the IP is flagged); HTTP 429 rate limiting; HTTP 5xx from Glints; a malformed GraphQL body the server rejects with 400; an anti-bot challenge page returned as the body.","commonSituations":"Running scans from a cloud/datacenter IP that Glints' WAF flags; scanning too aggressively and hitting 429; a Glints-side schema change making the query invalid (400); transient 5xx during their incident.","solutions":["Read the status and detail in the message: 429 → slow down (raise the inter-page delay / lower maxPages); 403 → the WAF is blocking this IP/UA, route through a residential egress or back off; 5xx → transient, retry.","Confirm BROWSER_LIKE_USER_AGENT and the origin/referer headers in graphqlPage() still match a current Chrome UA; update if Glints tightened fingerprinting.","Reduce pageSize or maxPages in portals.yml and add delay between runs to stay under rate limits.","If the detail shows a GraphQL validation error, re-capture the current searchJobsV3 query from a live browser session and update DEFAULT_GRAPHQL_QUERY."],"exampleFix":"# before (aggressive)\n- name: Glints (ID)\n  provider: glints\n  pageSize: 100\n  maxPages: 20\n\n# after (gentler, avoids 429/403)\n- name: Glints (ID)\n  provider: glints\n  pageSize: 30\n  maxPages: 3","handlingStrategy":"retry","validationCode":null,"typeGuard":"// Identify a retriable Glints HTTP error (transient 5xx / rate limit).\nfunction isRetriableGlintsError(err) {\n  const m = /glints: HTTP (\\d{3})/.exec(err.message || '');\n  if (!m) return false;\n  const s = Number(m[1]);\n  return s === 429 || s >= 500;\n}","tryCatchPattern":"// Retry transient Glints failures with backoff; surface the rest.\nasync function fetchGlintsResilient(entry, ctx) {\n  for (let attempt = 0; attempt < 3; attempt++) {\n    try { return await glintsProvider.fetch(entry, ctx); }\n    catch (err) {\n      if (attempt < 2 && isRetriableGlintsError(err)) {\n        await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));\n        continue;\n      }\n      throw err;\n    }\n  }\n}","preventionTips":["Keep pageSize/maxPages modest and honor the in-provider 300ms inter-page delay to avoid 429s.","Run Glints scans from an egress IP that Glints' WAF does not flag; residential is safest.","Keep BROWSER_LIKE_USER_AGENT current so requests are not blocked as non-browser."],"tags":["http-error","rate-limit","waf","glints","runtime","third-party-api"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}