jackwener/OpenCLI · error · ArgumentError

unknown city '${cityArg}'. pass a numeric cityId or one of:

Error message

unknown city '${cityArg}'. pass a numeric cityId or one of: ${names}

What it means

resolveCityId resolves the --city argument to a numeric dianping cityId by accepting a digit string or a key of the CITY_ID map (English or Chinese names). If the argument is non-numeric and not a known city key, the library throws this ArgumentError listing all accepted English city names. A null/empty argument is allowed and means 'use the cookie's default city', so this error only fires for a genuinely unrecognized name.

Source

Thrown at clis/dianping/utils.js:59

    xiamen: 15, '厦门': 15,
    hefei: 110, '合肥': 110,
};

export const SEARCH_COLUMNS = ['rank', 'shop_id', 'name', 'rating', 'reviews', 'price', 'cuisine', 'district', 'url'];
export const SHOP_COLUMNS = ['field', 'value'];

/**
 * Resolve a city argument (name or id) to a numeric cityId.
 * Returns null when the cookie's default city should be used.
 */
export function resolveCityId(cityArg) {
    if (cityArg == null || cityArg === '') return null;
    const raw = String(cityArg).trim().toLowerCase();
    if (/^\d+$/.test(raw)) return Number(raw);
    const id = CITY_ID[raw];
    if (!id) {
        const names = Object.keys(CITY_ID).filter((k) => /^[a-z]+$/.test(k)).join(', ');
        throw new ArgumentError(
            'city',
            `unknown city '${cityArg}'. pass a numeric cityId or one of: ${names}`,
        );
    }
    return id;
}

export function requireSearchLimit(value) {
    const raw = value == null || value === '' ? 15 : value;
    const limit = typeof raw === 'number' ? raw : Number(String(raw).trim());
    if (!Number.isInteger(limit) || limit < 1 || limit > 15) {
        throw new ArgumentError('limit must be an integer between 1 and 15 (dianping single page)');
    }
    return limit;
}

export function normalizeShopId(rawInput) {
    const raw = String(rawInput || '').trim();

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Use one of the listed English names exactly (beijing, shanghai, guangzhou, shenzhen, hangzhou, chengdu, chongqing, nanjing, suzhou, xian, wuhan, tianjin, qingdao, changsha, dalian, shenyang, kunming, fuzhou, xiamen, hefei).
  2. Look up the numeric cityId on dianping.com (/citylist or the /search/keyword/{cityId}/... URL) and pass the number instead — any digit string is accepted.
  3. Check for typos, extra whitespace, or full-width characters; the lookup lowercases and trims but does not fuzzy-match or accept pinyin variants.
  4. Extend the CITY_ID map (or open an issue/PR) if your city is supported by dianping but missing from the built-in list.

Example fix

// before
resolveCityId('shenzen'); // ArgumentError: unknown city
// after
resolveCityId('shenzhen'); // 7
// or pass the numeric id directly
resolveCityId('7'); // 7
Defensive patterns

Strategy: validation

Validate before calling

const KNOWN = ['beijing','shanghai','guangzhou','shenzhen','hangzhou','chengdu','chongqing','nanjing','suzhou','xian','wuhan','tianjin','qingdao','changsha','dalian','shenyang','kunming','fuzhou','xiamen','hefei'];
function isValidCityArg(v) {
  if (v == null || v === '') return true; // default city
  const raw = String(v).trim().toLowerCase();
  return /^\d+$/.test(raw) || KNOWN.includes(raw);
}

Type guard

function isCityArg(v) {
  return typeof v === 'string' && (/^\d+$/.test(v.trim()) || CITY_ID.hasOwnProperty(v.trim().toLowerCase()));
}

Try / catch

try {
  const cityId = resolveCityId(cityArg);
} catch (e) {
  if (e.name === 'ArgumentError' && String(e.message).startsWith('unknown city')) {
    console.error(`City '${cityArg}' not in built-in map; pass a numeric cityId from dianping /citylist`);
  } else throw e;
}

Prevention

When it happens

Trigger: Passing a city name not in the CITY_ID map (e.g. 'newyork', 'suzhou ' typos aside, 'guangdong', '东莞市', pinyin like 'shenzhenshi'), a misspelled name such as 'xian ' vs 'xian' works but 'xi\u2019an' or 'sian' does not, or passing a Chinese name variant that differs from the map keys.

Common situations: Typo in city name; passing a province or district instead of a supported city; passing a city dianping supports but this small built-in map omits (the map only has 20 common cities); locale/input-method issues producing full-width characters.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/7bcfbc80aa3b5531. Report an issue: GitHub.