{"record":{"id":"0f67b4980ca48542","repo":"jackwener/OpenCLI","slug":"title-must-be-a-single-line","errorCode":null,"errorMessage":"title must be a single line","messagePattern":"title must be a single line","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/codex/rename.js","lineNumber":26,"sourceCode":"} from './_actions.js';\n\ncli({\n    site: 'codex',\n    name: 'rename',\n    access: 'write',\n    description: 'Rename the selected Codex conversation. Opens the Chat actions menu → \"Rename chat\", then types the new title.',\n    domain: 'localhost',\n    strategy: Strategy.UI,\n    browser: true,\n    args: [\n        { name: 'title', required: true, positional: true, help: 'New title (single line, no newlines)' },\n        ...conversationSelectionArgs,\n    ],\n    columns: ['status', 'title', 'thread_id', 'project'],\n    func: async (page, kwargs) => {\n        const title = String(kwargs.title || '').trim();\n        if (!title) throw new ArgumentError('title cannot be empty');\n        if (title.includes('\\n')) throw new ArgumentError('title must be a single line');\n\n        // 1. Select the target chat AND click \"Rename chat\" in the menu.\n        const action = await selectAndClickAction(page, kwargs, ['Rename chat']);\n        await page.wait(0.5);\n\n        // 2. The rename input is the only non-ProseMirror editable that just appeared.\n        //    Fill it via execCommand insertText (Codex uses a contenteditable, not a plain input).\n        const filled = unwrapEvaluateResult(await page.evaluate(`(async () => {\n      const wait = (ms) => new Promise((r) => setTimeout(r, ms));\n      let input = null;\n      for (let attempt = 0; attempt < 15; attempt += 1) {\n        const candidates = Array.from(document.querySelectorAll('input[type=\"text\"], input:not([type]), [contenteditable=\"true\"]'))\n          .filter((el) => el.offsetParent && !el.classList.contains('ProseMirror'));\n        if (candidates.length) {\n          candidates.sort((a, b) => (a.getBoundingClientRect().left || 9999) - (b.getBoundingClientRect().left || 9999));\n          input = candidates[0];\n          break;\n        }","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/codex/rename.js#L8-L44","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ncodex rename \"Line one\nLine two\" 3\n// ArgumentError: title must be a single line\n\n// after\nconst title = rawTitle.replace(/\\s*\\n\\s*/g, ' ').trim();\ncodex rename title 3","handlingStrategy":"validation","validationCode":"const title = String(rawTitle ?? '').trim();\nif (title.includes('\\n')) {\n  throw new Error('Title must be a single line');\n}","typeGuard":"function isSingleLine(v) {\n  return typeof v === 'string' && v.trim().length > 0 && !v.includes('\\n');\n}","tryCatchPattern":"try {\n  await codexRename({ title, ...selection });\n} catch (e) {\n  if (e instanceof ArgumentError && /single line/.test(e.message)) {\n    return codexRename({ title: title.replace(/\\s*\\n\\s*/g, ' '), ...selection });\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["argument-validation","input-validation","codex"],"backgroundTag":"invalid-argument-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}