{"record":{"id":"dcc07ffb5a768110","repo":"santifer/career-ops","slug":"torre-unexpected-api-response-expected-result","errorCode":null,"errorMessage":"torre: unexpected API response — expected { results: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]","messagePattern":"torre: unexpected API response — expected (.+?), got keys: \\[(.+?)\\]","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/torre.mjs","lineNumber":218,"sourceCode":"    assertTorreUrl(SEARCH_ENDPOINT);\n    const body = JSON.stringify(buildTorreQuery(entry));\n    const fallbackCompany = entry?.name;\n\n    // Exactly one request: the endpoint caps at 20 rows and ignores every\n    // pagination form, so a loop could only refetch the same page (quirk 2).\n    // ctx.maxPages needs no handling for the same reason — one page is all\n    // there is, which is already what the health probe wants.\n    const url = `${SEARCH_ENDPOINT}?offset=0&size=${PAGE_SIZE}`;\n    // redirect:'error' prevents SSRF via server-side redirects\n    const json = await ctx.fetchJson(url, {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body,\n      redirect: 'error',\n    });\n\n    if (!json || !Array.isArray(json.results)) {\n      throw new Error(\n        `torre: unexpected API response — expected { results: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]`,\n      );\n    }\n\n    const out = [];\n    const seen = new Set();\n    for (const o of json.results) {\n      const normalized = normalizeTorreOpportunity(o, fallbackCompany);\n      if (!normalized || seen.has(normalized.url)) continue;\n      seen.add(normalized.url);\n      out.push(normalized);\n    }\n    return out;\n  },\n};\n","sourceCodeStart":200,"sourceCodeEnd":234,"githubUrl":"https://github.com/santifer/career-ops/blob/1696bec4d021768e7359f9aad6b329cba883da20/providers/torre.mjs#L200-L234","documentation":"After posting the search body to the Torre API, fetch() validates the JSON response has a 'results' array. Anything else — null body, an error object, a wrapped envelope — throws this error listing the keys actually received. This check fails the whole fetch (Torre does a single search request, unlike Muse's tolerant pagination).","triggerScenarios":"The Torre search POST returns a non-standard body: an auth/quota error object, an empty 204/empty-body response, a WAF/Cloudflare challenge page parsed as JSON, or an upstream schema change moving results into a nested field.","commonSituations":"Torre API outage or rate limiting; invalid/missing API credentials surfacing as an error-shaped JSON body; corporate proxy replacing responses; Torre changing its search response envelope after an API version bump.","solutions":["Read the keys printed in the message — they identify what came back (e.g. {error, message} points to an auth/quota problem).","Retry after a delay; transient 429/5xx responses often arrive as malformed bodies.","Verify API credentials, endpoint URL, and required headers for the Torre provider are current.","Check for proxy/WAF interference on your network if the body is an HTML/challenge payload.","If the envelope changed upstream, update the response parsing in providers/torre.mjs fetch() to the new schema."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isTorreSearchResponse(json) {\n  return !!json && typeof json === 'object' && !Array.isArray(json) &&\n    Array.isArray(json.results);\n}","tryCatchPattern":"try {\n  const jobs = await torreProvider.fetch(entry, ctx);\n} catch (err) {\n  if (String(err.message).startsWith('torre: unexpected API response')) {\n    console.warn('Torre search returned an unexpected body:', err.message);\n    // inspect keys in message; back off and retry, or check credentials\n  } else throw err;\n}","preventionTips":["Keep Torre API credentials and endpoint config current — error bodies usually mean auth/quota issues.","Watch for provider updates after any known Torre API version change.","Run a smoke search before batch scans to catch envelope changes early.","Investigate proxy/WAF interference if response bodies look like challenge pages."],"tags":["api","schema-mismatch","http","network"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"1696bec4d021768e7359f9aad6b329cba883da20","analyzedAt":"2026-09-01T19:19:23.111Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}