jackwener/OpenCLI · error · ArgumentError
title must be a single line
Error message
title must be a single line
What it means
Codex chat titles must be a single line; the library inspects the trimmed title and throws ArgumentError if it contains a newline character, because the rename input cannot accept multi-line text.
Source
Thrown at clis/codex/rename.js:26
} from './_actions.js';
cli({
site: 'codex',
name: 'rename',
access: 'write',
description: 'Rename the selected Codex conversation. Opens the Chat actions menu → "Rename chat", then types the new title.',
domain: 'localhost',
strategy: Strategy.UI,
browser: true,
args: [
{ name: 'title', required: true, positional: true, help: 'New title (single line, no newlines)' },
...conversationSelectionArgs,
],
columns: ['status', 'title', 'thread_id', 'project'],
func: async (page, kwargs) => {
const title = String(kwargs.title || '').trim();
if (!title) throw new ArgumentError('title cannot be empty');
if (title.includes('\n')) throw new ArgumentError('title must be a single line');
// 1. Select the target chat AND click "Rename chat" in the menu.
const action = await selectAndClickAction(page, kwargs, ['Rename chat']);
await page.wait(0.5);
// 2. The rename input is the only non-ProseMirror editable that just appeared.
// Fill it via execCommand insertText (Codex uses a contenteditable, not a plain input).
const filled = unwrapEvaluateResult(await page.evaluate(`(async () => {
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
let input = null;
for (let attempt = 0; attempt < 15; attempt += 1) {
const candidates = Array.from(document.querySelectorAll('input[type="text"], input:not([type]), [contenteditable="true"]'))
.filter((el) => el.offsetParent && !el.classList.contains('ProseMirror'));
if (candidates.length) {
candidates.sort((a, b) => (a.getBoundingClientRect().left || 9999) - (b.getBoundingClientRect().left || 9999));
input = candidates[0];
break;
}View on GitHub (pinned to 49907e53dc)
Solutions
- Strip or replace newlines before passing the title, e.g. title.replace(/\r?\n/g, ' ').
- Collapse the desired text to one line and truncate if needed.
- In shell, use echo "$TITLE" | tr '\n' ' ' or ${TITLE//$'\n'/ } before invoking the command.
Example fix
// before codex rename "Line one Line two" 3 // ArgumentError: title must be a single line // after const title = rawTitle.replace(/\s*\n\s*/g, ' ').trim(); codex rename title 3
Defensive patterns
Strategy: validation
Validate before calling
const title = String(rawTitle ?? '').trim();
if (title.includes('\n')) {
throw new Error('Title must be a single line');
} Type guard
function isSingleLine(v) {
return typeof v === 'string' && v.trim().length > 0 && !v.includes('\n');
} Try / catch
try {
await codexRename({ title, ...selection });
} catch (e) {
if (e instanceof ArgumentError && /single line/.test(e.message)) {
return codexRename({ title: title.replace(/\s*\n\s*/g, ' '), ...selection });
}
throw e;
} Prevention
- Normalize multi-line input with replace(/\s*\n\s*/g, ' ') before renaming.
- Avoid deriving titles from multi-line sources like files or commit bodies.
- Add a unit check for newline characters in user-supplied titles.
When it happens
Trigger: Calling codex rename with a title containing '\n', e.g. a multi-line string, a value pasted from a file or terminal selection that includes a line break, or a heredoc/unquoted variable with embedded newlines.
Common situations: Generating titles from multi-line text (commit messages, file contents); copying text from editors/terminals that include trailing newlines; using shell variables spanning multiple lines.
Related errors
- title cannot be empty
- --pick label cannot be empty
- ${label} must be a non-negative integer, got ${JSON.stringif
- limit must be a positive integer
- archive wayback timestamp must be YYYY[MM[DD[hh[mm[ss]]]]] o
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/0f67b4980ca48542.
Report an issue: GitHub.