{"record":{"id":"6df69f7595848765","repo":"santifer/career-ops","slug":"portals-yml-an-enabled-entry-has-no-name-careers-url-e","errorCode":null,"errorMessage":"portals.yml: an enabled entry has no name (careers_url: ${e.careers_url || 'none'}) — it cannot be provider-checked; give it a name","messagePattern":"portals\\.yml: an enabled entry has no name \\(careers_url: (.+?)\\) — it cannot be provider-checked; give it a name","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"verify-pipeline.mjs","lineNumber":563,"sourceCode":"    // CODE_ROOT (scan.mjs does the same). Without mergeProviderPlugins() the\n    // health check sees only providers/*.mjs and reports every enabled\n    // plugin-provider entry as an unknown provider that \"never scans\", while\n    // the scanner resolves and scans it (#4026). No-op for a plugin-free\n    // install: mergeProviderPlugins returns before any work when\n    // config/plugins.yml is absent.\n    const providers = await loadProviders(join(CODE_ROOT, 'providers'));\n    await mergeProviderPlugins(providers, { root: CODE_ROOT });\n    const { silent, handoff, unknownProvider } = findUnclaimedEntries(entries, providers);\n\n    // findUnclaimedEntries silently skips an entry with no (or blank) `name` —\n    // it can't report what it can't label. Without this, that entry vanishes\n    // from silent/handoff/unknownProvider entirely, and the enabled count\n    // below (which doesn't share the same eligibility rule) would still\n    // include it — so a malformed entry never gets a provider check AND the\n    // \"All N entries resolve\" success line claims it as resolved anyway.\n    const malformed = entries.filter(e => e && e.enabled !== false && (typeof e.name !== 'string' || !e.name.trim()));\n    for (const e of malformed) {\n      warn(`portals.yml: an enabled entry has no name (careers_url: ${e.careers_url || 'none'}) — it cannot be provider-checked; give it a name`);\n    }\n\n    for (const e of unknownProvider) {\n      error(`portals.yml: \"${e.name}\" sets an unknown provider — ${e.error}. The entry never scans (see providers/ for valid ids)`);\n    }\n    for (const e of silent) {\n      warn(`portals.yml: \"${e.name}\" is enabled but no provider claims ${e.careers_url || 'its careers_url'} — scan.mjs skips it on every run without naming it (run node audit-portals.mjs)`);\n    }\n    if (silent.length === 0 && unknownProvider.length === 0 && malformed.length === 0) {\n      const enabled = entries.filter(e => e && e.enabled !== false).length;\n      ok(handoff.length > 0\n        ? `All ${enabled - handoff.length} scannable portals.yml entries resolve to a provider (${handoff.length} on websearch handoff)`\n        : `All ${enabled} enabled portals.yml entries resolve to a provider`);\n    }\n  } catch (err) {\n    warn(`Portal coverage check could not run: ${err.message}`);\n  }\n}","sourceCodeStart":545,"sourceCodeEnd":581,"githubUrl":"https://github.com/santifer/career-ops/blob/e7abd431fce9348a95261acac9e0c14779c35df8/verify-pipeline.mjs#L545-L581","documentation":"verify-pipeline.mjs Check 15 warns about malformed portals.yml entries: an entry is enabled (enabled !== false) but has no usable `name` string. Such an entry can't be claimed by any provider, so it is excluded from provider checking and from the 'All N entries resolve' success count — it would silently never scan.","triggerScenarios":"Running `node verify-pipeline.mjs` after hand-editing portals.yml and adding an entry with a missing name, a name of \"\"/whitespace, or a name that isn't a string (e.g. a number or a nested map).","commonSituations":"YAML mistake like writing `name:` with no value; duplicating an entry and deleting the name line; a merge conflict resolved leaving the name out.","solutions":["Open portals.yml and give the listed entry a non-empty `name:` string","Ensure `name` is a plain scalar, not null or a nested block","Re-run verify-pipeline.mjs; the entry should then receive a provider check","Optionally run `node audit-portals.mjs` afterwards to confirm the board actually answers"],"exampleFix":"# before\n- careers_url: https://boards.greenhouse.io/acme\n  enabled: true\n# after\n- name: Acme\n  careers_url: https://boards.greenhouse.io/acme\n  enabled: true","handlingStrategy":"validation","validationCode":"const malformed = entries.filter(e => e && e.enabled !== false && (typeof e.name !== 'string' || !e.name.trim()));\nif (malformed.length) throw new Error('portals.yml entries missing name: ' + malformed.map(e => e.careers_url).join(', '));","typeGuard":"const hasName = (e) => e && typeof e.name === 'string' && e.name.trim().length > 0;","tryCatchPattern":null,"preventionTips":["Validate portals.yml after every edit (verify-pipeline.mjs check 15)","Never write `name:` without a value","Copy an existing entry as a template when adding companies","Keep one entry per YAML list item — avoid merge artifacts"],"tags":["config","yaml","validation"],"backgroundTag":"empty-required-field","analyzedSha":"e7abd431fce9348a95261acac9e0c14779c35df8","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}