{"record":{"id":"751a397168bc38d8","repo":"jackwener/OpenCLI","slug":"title-cannot-be-empty","errorCode":null,"errorMessage":"title cannot be empty","messagePattern":"title cannot be empty","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/codex/rename.js","lineNumber":25,"sourceCode":"    waitForConversationPostcondition,\n} 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;","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/codex/rename.js#L7-L43","documentation":"The codex rename command requires a --title argument; the library trims the provided value and throws ArgumentError when the result is an empty string, since a chat cannot be renamed to nothing.","triggerScenarios":"Calling the rename command with title missing, an empty string ('' or ' '), or a value that is only whitespace, so that String(kwargs.title || '').trim() yields ''.","commonSituations":"Forgetting the positional title argument on the CLI; passing a shell variable that is unset or empty; quoting issues where the shell strips the value; piping empty input.","solutions":["Pass a non-empty title, e.g. codex rename \"New Chat Title\" <conversation-selector>.","Trim the input yourself in a wrapper script and reject empty values before invoking the command.","Check shell quoting/variables to ensure the title argument actually reaches the command."],"exampleFix":"// before\n$ TITLE=\"\"; codex rename \"$TITLE\" 3\n// ArgumentError: title cannot be empty\n\n// after\n$ codex rename \"Sprint review notes\" 3","handlingStrategy":"validation","validationCode":"const title = String(process.argv[3] ?? '').trim();\nif (!title) {\n  throw new Error('Usage: codex rename <title> <conversation>');\n}","typeGuard":"function hasNonEmptyTitle(v) {\n  return typeof v === 'string' && v.trim().length > 0;\n}","tryCatchPattern":"try {\n  await codexRename({ title, ...selection });\n} catch (e) {\n  if (e instanceof ArgumentError) {\n    console.error('Provide a non-empty, single-line title.');\n    process.exitCode = 2;\n    return;\n  }\n  throw e;\n}","preventionTips":["Always quote the title argument in the shell.","Validate/trim title in wrapper scripts before invoking.","Check that interpolated variables are actually set."],"tags":["argument-validation","input-validation","codex"],"backgroundTag":"missing-or-empty-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}