jackwener/OpenCLI · error · ArgumentError

标题不能超过 30 字

Error message

标题不能超过 30 字

What it means

The draft command enforces Douyin's 30-character title limit client-side. If kwargs.title (required) exceeds 30 JavaScript string units, an ArgumentError is thrown. Note the check uses .length (UTF-16 code units), so some emoji count as 2.

Source

Thrown at clis/douyin/draft.js:317

        { name: 'video', required: true, positional: true, help: '视频文件路径' },
        { name: 'title', required: true, help: '视频标题(≤30字)' },
        { name: 'caption', default: '', help: '正文内容(≤1000字,支持 #话题)' },
        { name: 'cover', default: '', help: '封面图片路径' },
        { name: 'visibility', default: 'public', choices: ['public', 'friends', 'private'] },
    ],
    columns: ['status', 'draft_id'],
    func: async (page, kwargs) => {
        const videoPath = path.resolve(kwargs.video);
        if (!fs.existsSync(videoPath)) {
            throw new ArgumentError(`视频文件不存在: ${videoPath}`);
        }
        const ext = path.extname(videoPath).toLowerCase();
        if (!['.mp4', '.mov', '.avi', '.webm'].includes(ext)) {
            throw new ArgumentError(`不支持的视频格式: ${ext}(支持 mp4/mov/avi/webm)`);
        }
        const title = kwargs.title;
        if (title.length > 30) {
            throw new ArgumentError('标题不能超过 30 字');
        }
        const caption = kwargs.caption || '';
        if (caption.length > 1000) {
            throw new ArgumentError('正文不能超过 1000 字');
        }
        const coverPath = kwargs.cover;
        if (coverPath) {
            if (!fs.existsSync(path.resolve(coverPath))) {
                throw new ArgumentError(`封面文件不存在: ${path.resolve(coverPath)}`);
            }
        }
        if (!page.setFileInput) {
            throw new CommandExecutionError('当前浏览器适配器不支持文件注入', '请使用 Browser Bridge 或支持 setFileInput 的浏览器模式');
        }
        const visibilityLabel = VISIBILITY_LABELS[kwargs.visibility] ?? VISIBILITY_LABELS.public;
        await page.goto(DRAFT_UPLOAD_URL);
        await page.wait({ selector: 'input[type="file"]', timeout: 20 });
        await dismissKnownModals(page);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Shorten the title to 30 characters or fewer before invoking the command
  2. Trim programmatically: `[...title].slice(0, 30).join('')` (array spread counts code points, safer than .slice)
  3. If the title is built from a template, move variable text to caption (limit 1000)

Example fix

// before
const title = '我的超长标题……(40个字)';
// after
const title = [...rawTitle].slice(0, 30).join('');
Defensive patterns

Strategy: validation

Validate before calling

const title = [...rawTitle].join('');
if ([...title].length > 30) throw new Error('Title exceeds Douyin 30-char limit');

Try / catch

try {
  await cli.draft({ title, video });
} catch (e) {
  if (e instanceof ArgumentError && e.message.includes('标题不能超过')) {
    await cli.draft({ title: [...title].slice(0, 30).join(''), video });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling `douyin draft --title "..."` with a title whose .length > 30, typically long descriptive titles or titles heavy with emoji (each emoji can count as 2 units).

Common situations: Reusing titles written for YouTube/Twitter with longer limits; emoji-decorated titles; titles generated programmatically by concatenating tags.

Related errors


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