jackwener/OpenCLI · error · ArgumentError
Resume file output mismatch: expected ${expected.outputFile
Error message
Resume file output mismatch: expected ${expected.outputFile || 'in-memory mode'}, found ${outputFile || 'in-memory mode'} What it means
The command compares the resume file's resolved `outputFile` against the expected mode of the current run. If the stored state was produced with --output-file but the current run is in-memory (or vice versa, or a different path), readResumeFile throws this ArgumentError because the archive and resume state must agree.
Source
Thrown at clis/twitter/bookmarks.js:128
parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
}
catch (error) {
throw new CommandExecutionError(`Could not parse Twitter bookmarks resume file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
}
const count = parsed?.count;
const cursor = parsed?.cursor == null ? null : String(parsed.cursor);
const outputFile = parsed?.outputFile ? path.resolve(String(parsed.outputFile)) : null;
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)
|| !Number.isInteger(count) || count < 0
|| (parsed.cursor != null && typeof parsed.cursor !== 'string')
|| (cursor !== null && !cursor.trim())) {
throw new CommandExecutionError(`Twitter bookmarks resume file ${filePath} has an invalid shape`);
}
if (expected) {
if (parsed.source !== expected.source)
throw new ArgumentError(`Resume file source mismatch: expected ${expected.source}, found ${parsed.source || 'unknown'}`);
if (outputFile !== expected.outputFile)
throw new ArgumentError(`Resume file output mismatch: expected ${expected.outputFile || 'in-memory mode'}, found ${outputFile || 'in-memory mode'}`);
if (!expected.outputFile && !Array.isArray(parsed.tweets))
throw new CommandExecutionError(`Twitter bookmarks resume file ${filePath} is missing in-memory tweets`);
if (!expected.outputFile && parsed.tweets.length !== count)
throw new CommandExecutionError(`Twitter bookmarks resume file ${filePath} count does not match its in-memory tweets`);
if (parsed.complete)
throw new CommandExecutionError(`Twitter bookmarks resume file ${filePath} is already marked complete`);
}
return {
cursor,
count,
tweets: Array.isArray(parsed.tweets) ? parsed.tweets : [],
complete: Boolean(parsed.complete),
source: parsed.source || null,
outputFile,
updatedAt: parsed.updatedAt || null,
};
}
View on GitHub (pinned to 49907e53dc)
Solutions
- Re-run with the exact same --output-file (absolute path) that the resume file records.
- Delete the resume file and restart the sync with the desired mode.
- If intentionally switching modes, start over — never reuse a resume file across modes.
Example fix
// before (original run used --output-file): cli twitter bookmarks --all --resume-file ./b.json # in-memory resume -> mismatch // after: cli twitter bookmarks --all --resume-file ./b.json --output-file /abs/path/archive.jsonl
Defensive patterns
Strategy: validation
Validate before calling
const parsed = JSON.parse(fs.readFileSync(resumeFile, 'utf8'));
const recorded = parsed.outputFile ? path.resolve(parsed.outputFile) : null;
if (Boolean(recorded) !== Boolean(outputFile) || (recorded && outputFile && recorded !== path.resolve(outputFile))) {
throw new Error('resume/output-file mode mismatch');
} Type guard
function outputModeMatches(parsed, expectedOutputFile) {
const recorded = parsed.outputFile ? path.resolve(String(parsed.outputFile)) : null;
return recorded === (expectedOutputFile ? path.resolve(expectedOutputFile) : null);
} Try / catch
try {
await run(args);
} catch (e) {
if (String(e.message).includes('Resume file output mismatch')) {
const parsed = JSON.parse(fs.readFileSync(resumeFile, 'utf8'));
if (parsed.outputFile) args.push('--output-file', path.resolve(parsed.outputFile));
await run(args);
} else throw e;
} Prevention
- Store and pass absolute --output-file paths; the resume file records path.resolve'd paths.
- Never toggle between in-memory and --output-file mode mid-sync.
- If you move the archive, update or recreate the resume file.
- Script the whole flag set together so flags never drift apart.
When it happens
Trigger: Resuming with `--all --resume-file f` but omitting the --output-file that the original run used (or the reverse); passing a different/renamed --output-file path than the one recorded in the resume file; the resume file's outputFile was hand-edited.
Common situations: Moving the JSONL archive to another directory without updating the resume file; dropping --output-file on resume because 'the data is already in the archive'; relative vs absolute path confusion (the file stores a path.resolve'd absolute path).
Related errors
- Resume file source mismatch: expected ${expected.source}, fo
- Resume file source mismatch: expected ${expected.source}, fo
- Resume file username mismatch: expected @${expected.username
- Resume file output mismatch: expected ${expected.outputFile
- Unknown 12306 station telecode "${trimmed}"
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/9f952707b5182f38.
Report an issue: GitHub.