{"record":{"id":"2a26e3b8b0a8685f","repo":"santifer/career-ops","slug":"apify-entry-entry-name-has-invalid-field-map","errorCode":null,"errorMessage":"apify: entry ${entry.name} has invalid field_map. Each of title, url, company, location, description must be a string or a non-empty array of strings. title and url are required.","messagePattern":"apify: entry (.+?) has invalid field_map\\. Each of title, url, company, location, description must be a string or a non-empty array of strings\\. title and url are required\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/apify/index.mjs","lineNumber":195,"sourceCode":"    detect() { return null; },\n\n    async fetch(entry, ctx) {\n      const token = ctx?.env?.APIFY_TOKEN || process.env.APIFY_TOKEN;\n      if (!hasToken(token)) {\n        throw new Error('APIFY_TOKEN not set — enable apify in config/plugins.yml and add the token to .env');\n      }\n      if (!entry.actor) {\n        throw new Error(`apify: entry ${entry.name} missing 'actor' (e.g. misceres/indeed-scraper)`);\n      }\n      if (\n        !entry.field_map ||\n        !isFieldSpec(entry.field_map.title) ||\n        !isFieldSpec(entry.field_map.url) ||\n        (entry.field_map.company != null && !isFieldSpec(entry.field_map.company)) ||\n        (entry.field_map.location != null && !isFieldSpec(entry.field_map.location)) ||\n        (entry.field_map.description != null && !isFieldSpec(entry.field_map.description))\n      ) {\n        throw new Error(\n          `apify: entry ${entry.name} has invalid field_map. Each of title, url, company, ` +\n          `location, description must be a string or a non-empty array of strings. title and url are required.`\n        );\n      }\n\n      const opts = { token };\n      if (entry.timeout_ms != null) opts.timeoutMs = entry.timeout_ms;\n      const items = await runActor(entry.actor, entry.input || {}, opts);\n\n      const useLocalJd = entry.field_map.description != null;\n      const sourceLabel = String(entry.actor || 'apify').replace(/[^a-z0-9]+/gi, '-').toLowerCase();\n\n      return items\n        .map(item => {\n          const normalized = normalizeItem(item, entry.field_map, entry.defaults);\n          if (!normalized.title || !normalized.url) return null;\n          if (!isHttpsUrl(normalized.url)) return null;\n          if (!useLocalJd) return normalized;","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/plugins/apify/index.mjs#L177-L213","documentation":"Thrown by the apify provider's `fetch` hook (plugins/apify/index.mjs:195) when a portals.yml entry's `field_map` is missing, or when `title`/`url` are not valid field specs, or when any of the optional `company`/`location`/`description` fields (if present) are not valid field specs. A 'field spec' is a string or a non-empty array of strings (checked by `isFieldSpec`) naming the JSON path(s) in the actor's dataset items to extract. title and url are required; the others are optional but, if present, must be valid.","triggerScenarios":"The entry has no `field_map`, or `field_map.title`/`field_map.url` is missing/non-string/empty-array, or an optional field is set to a number/object/empty array. The compound `if` evaluates true and throws.","commonSituations":"field_map omitted entirely; title/url misspelled or referencing a field the actor doesn't output; an optional field set to `null`-equivalent or an empty array `[]`; YAML formatting that made a field an object instead of a string.","solutions":["Inspect a sample dataset item from the actor (Apify console) to learn the real field names, then set field_map accordingly.","Ensure `title` and `url` are strings (or non-empty arrays of strings) matching dataset keys.","For optional fields, either omit them or use valid string/array-of-strings specs.","Remove empty-array field_map entries (`[]` is invalid)."],"exampleFix":"# before — portals.yml\n- name: indeed\n  provider: apify\n  actor: misceres/indeed-scraper\n  field_map:\n    title: positionTitle   # wrong key name → empty extraction\n    url: []                # empty array → invalid\n# after (keys match the actor's dataset item shape)\n- name: indeed\n  provider: apify\n  actor: misceres/indeed-scraper\n  field_map:\n    title: title\n    url: url\n    company: company\n    location: location\n    description: description","handlingStrategy":"type-guard","validationCode":"function isFieldSpec(v) {\n  return typeof v === 'string' && v.length > 0 ||\n         (Array.isArray(v) && v.length > 0 && v.every(x => typeof x === 'string' && x.length > 0));\n}\nfunction validateFieldMap(entry) {\n  const fm = entry.field_map;\n  if (!fm || !isFieldSpec(fm.title) || !isFieldSpec(fm.url)) {\n    throw new Error(`Entry '${entry.name}' field_map requires valid title and url.`);\n  }\n  for (const k of ['company', 'location', 'description']) {\n    if (fm[k] != null && !isFieldSpec(fm[k])) {\n      throw new Error(`Entry '${entry.name}' field_map.${k} is invalid.`);\n    }\n  }\n}\nportals.filter(p => p.provider === 'apify').forEach(validateFieldMap);","typeGuard":"/** @param {unknown} v */\nfunction isFieldSpec(v) {\n  if (typeof v === 'string') return v.length > 0;\n  if (Array.isArray(v)) return v.length > 0 && v.every(x => typeof x === 'string' && x.length > 0);\n  return false;\n}","tryCatchPattern":"try {\n  await provider.fetch(entry, ctx);\n} catch (err) {\n  if (/invalid field_map/.test(err.message)) {\n    console.error(`Config error: ${err.message}`);\n    process.exitCode = 2;\n  } else throw err;\n}","preventionTips":["Inspect a sample dataset item on the Apify console to learn real field names before writing field_map.","Lint field_map with isFieldSpec in CI for every apify entry.","Omit optional fields rather than setting them to empty arrays."],"tags":["apify","configuration","validation","field-map","plugin"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}