{"record":{"id":"b37ed0a64eef4519","repo":"jackwener/OpenCLI","slug":"label-is-required-b37ed0","errorCode":null,"errorMessage":"${label} is required","messagePattern":"(.+?) is required","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/salesnav-search.js","lineNumber":20,"sourceCode":"import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';\nimport { unwrapEvaluateResult } from './shared.js';\n\nconst LINKEDIN_DOMAIN = 'www.linkedin.com';\nconst SALES_HOME = 'https://www.linkedin.com/sales/';\nconst LEAD_SEARCH_BASE = 'https://www.linkedin.com/sales-api/salesApiLeadSearch';\n// Versioned response decoration. LinkedIn bumps this on Sales Navigator\n// redeploys; if the response shape ever changes, refresh it from a live\n// /sales/search/people request.\nconst LEAD_SEARCH_DECORATION = 'com.linkedin.sales.deco.desktop.searchv2.LeadSearchResult-14';\nconst PAGE_SIZE = 25;\n\nfunction normalizeWhitespace(value) {\n  return String(value ?? '').replace(/[  ]/g, ' ').replace(/\\s+/g, ' ').trim();\n}\n\nfunction requireStringArg(args, key, label = key) {\n  const value = normalizeWhitespace(args[key]);\n  if (!value) throw new ArgumentError(`${label} is required`);\n  return value;\n}\n\nfunction parseLimit(value) {\n  if (value === undefined || value === null || value === '') return 25;\n  const limit = Number(value);\n  if (!Number.isInteger(limit) || limit < 1 || limit > 500) {\n    throw new ArgumentError('--limit must be an integer between 1 and 500');\n  }\n  return limit;\n}\n\n// Sales Navigator keeps the structural ( ) , : of the query literal and only\n// percent-encodes the keyword value.\nfunction leadSearchUrl(keywords, start) {\n  const query = '(spellCorrectionEnabled:true,recentSearchParam:(doLogHistory:true),keywords:'\n    + encodeURIComponent(keywords) + ')';\n  return LEAD_SEARCH_BASE","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/salesnav-search.js#L2-L38","documentation":"requireStringArg normalizes whitespace on the named CLI argument and throws ArgumentError if the value is empty or missing. It guarantees downstream query-building code (e.g. Sales Navigator keyword search) always receives a non-empty string.","triggerScenarios":"Invoking a salesnav-search command without the required argument (e.g. no --keywords), or passing only whitespace/nbsp characters so the normalized value is an empty string.","commonSituations":"Scripted calls with an unset shell variable (--keywords \"$QUERY\" where QUERY is empty); users quoting an empty string; typo in the flag name so the expected key is absent from args.","solutions":["Pass the required argument, e.g. --keywords \"engineer AND sales\".","Echo the shell variable before invoking to confirm it is non-empty.","Fix the flag name/kebab-case spelling to match the CLI's expected key."],"exampleFix":"// before\nawait run(['salesnav-search']); // missing keywords\n// after\nawait run(['salesnav-search', '--keywords', 'engineer AND sales']);","handlingStrategy":"validation","validationCode":"const keywords = (args.keywords ?? '').replace(/[\\u00a0\\u202f]/g, ' ').replace(/\\s+/g, ' ').trim();\nif (!keywords) throw new Error('--keywords is required before invoking the CLI');","typeGuard":"function hasKeywords(args) {\n  return typeof args.keywords === 'string' && args.keywords.trim().length > 0;\n}","tryCatchPattern":null,"preventionTips":["Default non-empty keyword values in wrapper scripts","Validate CLI args in your wrapper before spawning","Watch for invisible whitespace (nbsp) in copied query strings","Use set -u / ${VAR:?} in shell scripts to catch unset variables"],"tags":["argument-validation","cli","missing-argument"],"backgroundTag":"missing-required-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}