{"record":{"id":"a91e858947c01e43","repo":"jackwener/OpenCLI","slug":"shop-id-must-be-a-non-empty-string","errorCode":null,"errorMessage":"shop_id must be a non-empty string","messagePattern":"shop_id must be a non-empty string","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/dianping/utils.js","lineNumber":78,"sourceCode":"            'city',\n            `unknown city '${cityArg}'. pass a numeric cityId or one of: ${names}`,\n        );\n    }\n    return id;\n}\n\nexport function requireSearchLimit(value) {\n    const raw = value == null || value === '' ? 15 : value;\n    const limit = typeof raw === 'number' ? raw : Number(String(raw).trim());\n    if (!Number.isInteger(limit) || limit < 1 || limit > 15) {\n        throw new ArgumentError('limit must be an integer between 1 and 15 (dianping single page)');\n    }\n    return limit;\n}\n\nexport function normalizeShopId(rawInput) {\n    const raw = String(rawInput || '').trim();\n    if (!raw) throw new ArgumentError('shop_id must be a non-empty string');\n\n    const idMatch = raw.match(/\\/shop\\/([^?#/]+)/);\n    const shopId = idMatch ? idMatch[1] : raw;\n    if (!/^[A-Za-z0-9_-]+$/.test(shopId)) {\n        throw new ArgumentError(`'${raw}' does not look like a dianping shop id`);\n    }\n    return shopId;\n}\n\nexport function wrapDianpingStep(label, fn) {\n    return Promise.resolve()\n        .then(fn)\n        .catch((err) => {\n            if (err?.code) throw err;\n            const message = err?.message || String(err);\n            throw new CommandExecutionError(`dianping ${label} failed: ${message}`);\n        });\n}","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/dianping/utils.js#L60-L96","documentation":"normalizeShopId accepts either a bare shop id or a dianping shop URL (/shop/<id>) and extracts the id. This specific error is thrown when the input is empty or falsy after string coercion — e.g. null, undefined, '', or a placeholder that trims to nothing — since no id can be derived. Note that a non-empty input failing the id pattern check throws a different error ('does not look like a dianping shop id').","triggerScenarios":"Calling the shop command with shop_id='' or whitespace only; passing an unset variable (undefined/null) from a script; piping an empty lookup result from a prior step into shop_id; a template string that interpolates to ''.","commonSituations":"Chained automation where a previous search returned no rows so the id variable is empty; shell variable not exported/quoted; reading an empty CSV/JSON field; forgetting to pass the --shop-id flag and an empty default being used.","solutions":["Pass a real dianping shop id (e.g. from search results' shop_id column) or a full shop URL like https://www.dianping.com/shop/1234567.","Verify the upstream step actually produced an id — re-run the search and confirm the shop_id field is populated before chaining.","Guard the call: if (!shopId || !shopId.trim()) fail early with a clear message instead of invoking the command.","Check shell/config plumbing — quoting, exported variables, and non-empty CSV/JSON fields."],"exampleFix":"// before\nconst id = row.shop_id; // '' when search returned junk\nawait shop(id); // ArgumentError: shop_id must be a non-empty string\n// after\nif (!row?.shop_id?.trim()) throw new Error('upstream search returned no shop_id');\nawait shop(row.shop_id.trim());","handlingStrategy":"validation","validationCode":"function hasUsableShopId(v) {\n  const raw = String(v ?? '').trim();\n  if (!raw) return false;\n  const m = raw.match(/\\/shop\\/([^?#/]+)/);\n  const id = m ? m[1] : raw;\n  return /^[A-Za-z0-9_-]+$/.test(id);\n}","typeGuard":"function isShopIdInput(v) {\n  return typeof v === 'string' && v.trim().length > 0;\n}","tryCatchPattern":"try {\n  const id = normalizeShopId(input);\n} catch (e) {\n  if (e.name === 'ArgumentError' && /non-empty string/.test(e.message)) {\n    console.error('No shop id provided — check upstream search output / flag plumbing');\n  } else if (e.name === 'ArgumentError') {\n    console.error(`'${input}' is not a shop id or /shop/ URL`);\n  } else throw e;\n}","preventionTips":["Check upstream results are non-empty before chaining shop lookups on row.shop_id.","Pass full /shop/ URLs or bare ids; both are accepted by normalizeShopId.","Quote and export shell variables so empty values fail loudly at your own boundary.","Fail early in scripts: if (!shopId?.trim()) throw before invoking the command."],"tags":["validation","argument-error","shop-id"],"backgroundTag":"missing-required-parameter","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}