jackwener/OpenCLI · error · TimeoutError

Douyin transcode for video

Error message

Douyin transcode for video ${videoId}

What it means

pollTranscodeWithFetch polls Douyin's transcode status endpoint until encode=2 or the deadline passes; on timeout it throws this TimeoutError naming the videoId, meaning the video never finished transcoding within timeoutMs.

Solutions

  1. Increase the timeoutMs parameter and retry polling
  2. Verify videoId is correct and check the video's status manually on creator.douyin.com
  3. Re-check auth/cookies if status calls are failing so real progress is visible
  4. Re-upload the video if the transcode job is permanently stuck server-side

Example fix

// before
await pollTranscode(page, videoId); // default timeout
// after
await pollTranscode(page, videoId, { timeoutMs: 10 * 60 * 1000 });
Defensive patterns

Strategy: retry

Validate before calling

if (!Number.isInteger(videoId) && !/^\d+$/.test(String(videoId))) {
  throw new Error(`invalid videoId: ${videoId}`);
}

Type guard

function isValidVideoId(v) {
  return typeof v === 'string' && /^\d+$/.test(v);
}

Try / catch

import { TimeoutError } from './_shared/transcode.js';
try { await pollTranscode(page, videoId); }
catch (e) {
  if (e instanceof TimeoutError) {
    console.warn(`transcode still pending for ${videoId}; extending deadline`);
    await pollTranscode(page, videoId, { timeoutMs: 15 * 60 * 1000 });
  } else throw e;
}

Prevention

When it happens

Trigger: pollTranscodeWithFetch(videoId, ...) called via pollTranscode loops past the deadline because the video is stuck in transcoding (encode never becomes 2) or polling requests keep failing silently.

Common situations: Very large/long videos exceeding the default timeout; Douyin backend congestion; invalid videoId so status never reaches done; authenticated polling requests rejected so status stays unknown.

Related errors


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

Appendix: source

Thrown at clis/douyin/_shared/transcode.js:31

/**
 * Lower-level poll function that accepts an injected fetch function.
 * Exported for testability.
 */
export async function pollTranscodeWithFetch(fetchFn, page, videoId, timeoutMs = DEFAULT_TIMEOUT_MS) {
    const url = `${TRANSCODE_URL_BASE}?video_id=${encodeURIComponent(videoId)}&aid=1128`;
    const deadline = Date.now() + timeoutMs;
    while (Date.now() < deadline) {
        const result = (await fetchFn(page, 'GET', url));
        if (result.encode === 2) {
            return result;
        }
        // Wait before next poll, but don't exceed the deadline
        const remaining = deadline - Date.now();
        if (remaining <= 0)
            break;
        await new Promise(resolve => setTimeout(resolve, Math.min(POLL_INTERVAL_MS, remaining)));
    }
    throw new TimeoutError(`Douyin transcode for video ${videoId}`, Math.round(timeoutMs / 1000));
}
/**
 * Poll Douyin's transcode status endpoint until the video is fully transcoded
 * (encode=2) or the timeout expires.
 *
 * @param page - Browser page for making credentialed API calls
 * @param videoId - The video_id returned from the confirm upload step
 * @param timeoutMs - Maximum wait time in ms (default: 300 000 = 5 minutes)
 * @returns TranscodeResult including duration, fps, dimensions, and poster info
 * @throws TimeoutError if transcode does not complete within timeoutMs
 */
export async function pollTranscode(page, videoId, timeoutMs) {
    return pollTranscodeWithFetch(browserFetch, page, videoId, timeoutMs);
}

View on GitHub (pinned to 49907e53dc)