jackwener/OpenCLI · error · ArgumentError

--${name} is not a valid place name: ${JSON.stringify(raw)}

Error message

--${name} is not a valid place name: ${JSON.stringify(raw)}

What it means

After the presence check, parsePlaceName rejects values longer than 20 characters or containing ASCII control characters (\x00-\x1f, including newlines and tabs), since the list queries pass the raw Chinese place name into URLs. The rejected input is echoed JSON-escaped in the ArgumentError message.

Source

Thrown at clis/ctrip/utils.js:524

      })()
    `;
}

/** Validate a 1-50 result limit shared by the browser-mode list commands (default 20). */
export function parseListLimit(raw, fallback = 20) {
    return parseStrictIntegerRange('limit', raw, fallback);
}

/** Validate a Chinese place keyword (station, city, port, or destination) for the browser list queries. */
export function parsePlaceName(name, raw) {
    if (raw === undefined || raw === null || String(raw).trim() === '') {
        throw new ArgumentError(`--${name} is required (e.g. 北京 / 上海)`);
    }
    const value = String(raw).trim();
    // These list pages key on the raw Chinese place name; reject control
    // characters and over-long input rather than passing them through.
    if (value.length > 20 || /[\x00-\x1f]/.test(value)) {
        throw new ArgumentError(`--${name} is not a valid place name: ${JSON.stringify(raw)}`);
    }
    return value;
}

export function buildTrainListUrl(fromName, toName, date) {
    const params = new URLSearchParams({
        dStationName: fromName,
        aStationName: toName,
        dDate: date,
        ticketType: '1',
    });
    return `https://trains.ctrip.com/webapp/train/list?${params.toString()}`;
}

/**
 * Browser-context IIFE that extracts train rows from the trains.ctrip.com
 * list page. Each `.card-white.list-item` exposes stable, class-keyed leaf
 * fields (`.from/.mid/.to/.rbox/.surplus-list`), so we read by selector rather

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Shorten the value to the bare Chinese place name (<= 20 chars).
  2. Strip newlines/tabs/control characters: value.replace(/[\x00-\x1f]/g, '').trim().
  3. Fix scripts that interpolate untrimmed command output into the flag.

Example fix

// before
ctrip trains --from-city "上海市浦东新区世纪大道1000号"
// after
ctrip trains --from-city 上海
Defensive patterns

Strategy: validation

Validate before calling

const name = String(raw).trim();
if (name.length > 20 || /[\x00-\x1f]/.test(name)) {
  throw new Error('place name must be <=20 chars with no control characters: ' + JSON.stringify(raw));
}

Type guard

const isValidPlaceName = (v) => typeof v === 'string' && v.trim().length >= 1 && v.trim().length <= 20 && !/[\x00-\x1f]/.test(v.trim());

Try / catch

try {
  const name = parsePlaceName('to-city', raw);
} catch (e) {
  if (e instanceof ArgumentError && e.message.includes('not a valid place name')) {
    const cleaned = String(raw).replace(/[\x00-\x1f]/g, '').trim().slice(0, 20);
    return parsePlaceName('to-city', cleaned);
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing --from-city/--to-city/--port/--destination values that exceed 20 chars or embed control characters: multi-line paste, tab/newline inside the name, strings with stray escapes, or overly long descriptions instead of a place keyword.

Common situations: Copy-pasting a whole address or sentence from notes into the flag, pasting names with trailing newline from terminal output (e.g. $(...)), or script bugs that join multiple values with '\n'.

Understand the failure class

Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.

Related errors


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