jackwener/OpenCLI · error · CommandExecutionError
Failed to parse 12306 station_name.js: no station records fo
Error message
Failed to parse 12306 station_name.js: no station records found
What it means
CommandExecutionError from parseStationBundle when the extracted quoted string exists but splits into zero usable station records. Each record must have at least 8 `|`-separated fields including a telecode at position 2; if the string is present but empty or malformed, the stations array stays empty and this error is thrown.
Source
Thrown at clis/12306/utils.js:62
throw new CommandExecutionError('Failed to parse 12306 station_name.js: source string not found');
}
const raw = match[1];
const records = raw.split('@').filter(Boolean);
const stations = [];
for (const r of records) {
const parts = r.split('|');
if (parts.length < 8 || !parts[2]) continue;
stations.push({
short: parts[0] || '',
name: parts[1] || '',
code: parts[2] || '',
pinyin: parts[3] || '',
abbr: parts[4] || '',
city: parts[7] || '',
});
}
if (stations.length === 0) {
throw new CommandExecutionError('Failed to parse 12306 station_name.js: no station records found');
}
return stations;
}
/**
* Resolve a user-supplied station identifier to a telecode.
*
* Accepts Chinese name (`上海虹桥`), telecode (`AOH`), pinyin
* (`shanghaihongqiao`), short alias (`shh`), or city name with a
* preference for the city's main station.
*/
export function resolveStation(stations, input) {
const trimmed = String(input ?? '').trim();
if (!trimmed) throw new ArgumentError('station must not be empty');
if (STATION_CODE_RE.test(trimmed)) {
const exact = stations.find((s) => s.code === trimmed);
if (exact) return exact;
throw new ArgumentError(`Unknown 12306 station telecode "${trimmed}"`);View on GitHub (pinned to 49907e53dc)
Solutions
- Re-fetch — a truncated or stub response is often transient; clear any intermediary cache/proxy.
- Dump the matched string (first ~200 chars) and verify records are `@`-separated with 8+ pipe fields.
- If 12306 changed the bundle layout, update the field positions and the parts.length/telecode guards in parseStationBundle.
- Pin or cache a known-good station bundle as a fallback.
Example fix
// before
if (stations.length === 0) {
throw new CommandExecutionError('Failed to parse 12306 station_name.js: no station records found');
}
// after
if (stations.length === 0) {
throw new CommandExecutionError(`Failed to parse 12306 station_name.js: no station records found (extracted ${raw.length} chars, sample: ${raw.slice(0, 100)})`);
} Defensive patterns
Strategy: type-guard
Validate before calling
const match = text.match(/'([^']+)'/);
const records = match ? match[1].split('@').filter(Boolean) : [];
const ok = records.some((r) => r.split('|').length >= 8 && r.split('|')[2]);
if (!ok) console.warn('station bundle present but structurally empty/malformed'); Type guard
function hasValidStationRecords(text) {
const m = typeof text === 'string' && text.match(/'([^']+)'/);
if (!m) return false;
return m[1].split('@').some((r) => {
const p = r.split('|');
return p.length >= 8 && Boolean(p[2]);
});
} Try / catch
try {
const stations = await fetchStationBundle();
} catch (e) {
if (/no station records found/.test(e.message)) {
console.error('Bundle malformed/truncated — refetching or falling back to cached bundle');
const stations = parseStationBundle(await fs.readFile('./station_name.cache.js', 'utf8'));
} else throw e;
} Prevention
- Cache a known-good station bundle as a fallback.
- Spot-check the extracted string format (`@`-separated, 8+ pipe fields) when errors occur.
- Watch for 12306 bundle-format changes and update field indices in parseStationBundle.
- Re-fetch on failure — truncated/stub responses are often transient.
When it happens
Trigger: station_name.js served a quoted but empty or structurally changed string — e.g. a stub file with `var station_names ='';`, a truncated download, or a 12306 format change that alters field positions/count so every record fails the `parts.length < 8 || !parts[2]` guard.
Common situations: CDN serving a truncated/cached placeholder file; 12306 mid-deploy serving a stub bundle; a protocol change in the bundle format (fewer fields per record); a proxy compressing/altering the body such that the quoted literal is mangled.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Twitter device-follow response was missing the expected time
- 12306 ${endpoint} returned an unexpected payload shape
- Ctrip flight API returned a malformed batchSearch payload
- Ctrip flight API returned malformed itinerary at index ${ind
- LinkedIn Learning searchV2 returned no parseable rows with s
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/a16f2d17bbb200a9.
Report an issue: GitHub.