{"record":{"id":"5ac2307377f0ce25","repo":"jackwener/OpenCLI","slug":"label-cannot-be-empty-5ac230","errorCode":null,"errorMessage":"${label} cannot be empty","messagePattern":"(.+?) cannot be empty","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/codex/sidebar.js","lineNumber":51,"sourceCode":"    }\n    const parsed = Number.parseInt(value, 10);\n    if (!Number.isSafeInteger(parsed) || parsed < 1) {\n        throw new ArgumentError(`${label} must be a positive integer`);\n    }\n    return parsed;\n}\n\nexport function parseOptionalPositiveIntegerOption(raw, label) {\n    if (raw == null || cleanText(raw) === '') {\n        return null;\n    }\n    return parsePositiveIntegerOption(raw, label);\n}\n\nexport function requireNonEmptyOption(raw, label) {\n    const value = cleanText(raw);\n    if (!value) {\n        throw new ArgumentError(`${label} cannot be empty`);\n    }\n    return value;\n}\n\nexport function collectCodexProjectsFromDocument(doc = document) {\n    const projectRowSelector = '[data-app-action-sidebar-project-row]';\n    const threadRowSelector = '[data-app-action-sidebar-thread-row]';\n\n    function visibleText(el) {\n        return (el.innerText || el.textContent || '').replace(/\\s+/g, ' ').trim();\n    }\n\n    function isRelativeTime(text) {\n        return /^(?:(?:\\d+\\s*)?(?:刚刚|秒|分钟|小时|天|周|个月|年|sec|min|hr|hour|day|week|month|year|s|m|h|d|w)|.*\\bago)$/i.test(text.trim());\n    }\n\n    function getUpdatedText(row, title) {\n        const candidates = Array.from(row.querySelectorAll('.tabular-nums, [class*=\"tabular-nums\"], [class*=\"description\"]'))","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/codex/sidebar.js#L33-L69","documentation":"`requireNonEmptyOption` rejects option values that are empty (or whitespace-only) after `cleanText` normalization. It guarantees required string options (like thread ids or labels) always carry a usable value.","triggerScenarios":"Calling `threadId` / `requireNonEmptyOption` with `''`, `'   '`, or `null`/`undefined` — e.g. `--thread-id \"\"` from an unset shell variable, or empty config value interpolated into the option.","commonSituations":"Unset environment variables expanded to empty (`--thread-id \"$CODEX_THREAD\"` with CODEX_THREAD unset); whitespace-only pasted ids; empty CLI defaults.","solutions":["Supply a non-empty value for the option, e.g. a real thread id.","Check the shell variable/config source is actually set before invoking.","Guard in your wrapper: skip the option entirely if the value is empty instead of passing an empty string.","Trim user input before forwarding."],"exampleFix":"// before\nawait sidebar({ threadId: process.env.CODEX_THREAD }); // unset -> ''\n// after\nconst tid = process.env.CODEX_THREAD?.trim();\nif (tid) await sidebar({ threadId: tid });","handlingStrategy":"validation","validationCode":"function hasNonEmptyOption(v) {\n  return typeof v === 'string' && v.trim().length > 0;\n}\nif (!hasNonEmptyOption(rawThreadId)) throw new Error('thread-id is required and cannot be empty');","typeGuard":"const isNonEmptyText = (v) => typeof v === 'string' && v.trim() !== '';","tryCatchPattern":"try {\n  await sidebarCmd({ 'thread-id': raw });\n} catch (e) {\n  if (String(e.message).endsWith('cannot be empty')) {\n    console.error(`Option '${label}' was empty — check its shell variable/config source`);\n  } else throw e;\n}","preventionTips":["Never pass unset env variables directly: `${VAR:?}` in bash or check in JS.","Omit the option entirely rather than passing '' when optional.","Trim pasted ids to remove stray whitespace.","Default-check config files for empty values before invoking."],"tags":["validation","argument","codex","empty-input"],"backgroundTag":"missing-or-empty-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}