jackwener/OpenCLI · error · ArgumentError

title cannot be empty

Error message

title cannot be empty

What it means

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.

Source

Thrown at clis/codex/rename.js:25

    waitForConversationPostcondition,
} 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

  1. Pass a non-empty title, e.g. codex rename "New Chat Title" <conversation-selector>.
  2. Trim the input yourself in a wrapper script and reject empty values before invoking the command.
  3. Check shell quoting/variables to ensure the title argument actually reaches the command.

Example fix

// before
$ TITLE=""; codex rename "$TITLE" 3
// ArgumentError: title cannot be empty

// after
$ codex rename "Sprint review notes" 3
Defensive patterns

Strategy: validation

Validate before calling

const title = String(process.argv[3] ?? '').trim();
if (!title) {
  throw new Error('Usage: codex rename <title> <conversation>');
}

Type guard

function hasNonEmptyTitle(v) {
  return typeof v === 'string' && v.trim().length > 0;
}

Try / catch

try {
  await codexRename({ title, ...selection });
} catch (e) {
  if (e instanceof ArgumentError) {
    console.error('Provide a non-empty, single-line title.');
    process.exitCode = 2;
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: 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 ''.

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/751a397168bc38d8. Report an issue: GitHub.