yikart/AiToEarn · error · BadRequestException
初始化视频发布失败,缺少publish_id或upload_url
Error message
初始化视频发布失败,缺少publish_id或upload_url
What it means
This BadRequestException is thrown inside TikTokService.initVideoPublish when TikTok's /v2/post/publish/video/init/ call returns HTTP 200 but the response body data.data lacks either publish_id or upload_url. It is a response-shape validation guard: the TikTok Content Posting API sometimes returns a success status with an incomplete payload (e.g. error object present at another level, or a different publish flow), and this code fails fast instead of returning unusable init data. Note this inner throw is itself caught by the surrounding catch block, which rewraps it as the message at error 422/423.
Source
Thrown at project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.service.ts:224
// 添加视频封面时间戳,如果提供了
if (videoInfo.videoCoverTimestampMs) {
requestBody.post_info.video_cover_timestamp_ms = videoInfo.videoCoverTimestampMs;
}
this.logger.debug('初始化视频发布请求:', JSON.stringify(requestBody));
const { data } = await firstValueFrom(
this.httpService.post(`${this.apiBaseUrl}/v2/post/publish/video/init/`, requestBody, {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
}
})
);
if (!data.data.publish_id || !data.data.upload_url) {
throw new BadRequestException('初始化视频发布失败,缺少publish_id或upload_url');
}
return data.data;
} catch (error) {
this.logger.error('初始化TikTok视频发布失败:', error.response?.data || error.message);
throw new BadRequestException(`初始化视频发布失败: ${error.response?.data?.error?.message || error.message}`);
}
}
/**
* 上传视频文件
* @param accessToken 访问令牌
* @param videoBuffer 视频文件缓冲区
* @param initData 初始化返回的数据
* @returns 上传结果
*/
async uploadVideo(
accessToken: string,View on GitHub (pinned to d3aa8bea5b)
Solutions
- Log the full raw response body (JSON.stringify(data)) at this point to see what TikTok actually returned before assuming a missing field.
- Check data.error / data.data.error for a TikTok error envelope returned with HTTP 200 and surface its message instead of proceeding.
- Verify the app's publish scope (video.publish) is approved for production; sandbox apps may get success-with-limited payloads.
- Confirm privacy_level and post_info values are valid for your app's audit status (invalid values can yield incomplete init responses).
- If TikTok changed the response shape, update parsing to the current Content Posting API v2 schema (publish_id vs upload_url availability per source type) and pin the API version.
Example fix
// before: reading nested fields that may not exist
if (!data.data.publish_id || !data.data.upload_url) {
throw new BadRequestException('初始化视频发布失败,缺少publish_id或upload_url');
}
// after: guard against missing data envelope and surface TikTok error info
const initData = data?.data;
if (!initData || !initData.publish_id || !initData.upload_url) {
const apiErr = data?.error ?? initData?.error;
throw new BadRequestException(
`初始化视频发布失败,缺少publish_id或upload_url: ${apiErr?.message ?? JSON.stringify(data).slice(0, 500)}`
);
} Defensive patterns
Strategy: type-guard
Validate before calling
function isCompletePublishInit(res: any): res is { data: { data: { publish_id: string; upload_url: string } } } {
return !!res?.data?.data &&
typeof res.data.data.publish_id === 'string' && res.data.data.publish_id.length > 0 &&
typeof res.data.data.upload_url === 'string' && res.data.data.upload_url.length > 0;
} Type guard
function hasPublishInitData(v: unknown): v is { publish_id: string; upload_url: string } {
return typeof v === 'object' && v !== null &&
typeof (v as any).publish_id === 'string' && (v as any).publish_id.length > 0 &&
typeof (v as any).upload_url === 'string' && (v as any).upload_url.length > 0;
} Try / catch
try {
const initData = await tiktokService.initVideoPublish(accessToken, videoSize, videoInfo);
if (!hasPublishInitData(initData)) {
// HTTP 200 but incomplete payload: inspect raw response / TikTok error envelope before proceeding
throw new Error('init 返回不完整:缺少 publish_id 或 upload_url');
}
} catch (e) {
logger.warn('initVideoPublish failed', e);
throw e;
} Prevention
- Log the entire raw response body on init, not just assumed fields, so contract changes are visible immediately.
- Check for a TikTok error envelope returned with HTTP 200 (data.error) before trusting data.data.
- Pin/monitor the TikTok Content Posting API version you target and review changelogs for response-shape changes.
- Use video.publish only with audit-approved privacy_level values valid for your app tier.
- Treat a 200 response as untrusted input: validate publish_id/upload_url before persisting or using them.
When it happens
Trigger: initVideoPublish receives a 2xx response from POST ${apiBaseUrl}/v2/post/publish/video/init/ where data.data.publish_id is undefined/null/empty OR data.data.upload_url is undefined/null/empty — e.g. TikTok returned an error envelope with HTTP 200, returned only publish_id for PULL_FROM_URL-style flows, or the JSON structure changed (fields moved/renamed in a newer API version).
Common situations: Seen when TikTok silently degrades the response (draft mode apps, unpublished/unaudited privacy_level values like PUBLIC rejected for sandbox apps), when the response is data.error instead of data.data (so data.data is undefined and reading .publish_id on it throws or is falsy), after TikTok API version migrations that drop upload_url for certain sources, or when a proxy/gateway returns a 200 HTML/JSON page that doesn't match the expected schema.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- 初始化视频上传失败: ${error.response?.data?.error?.message || error.m
- 初始化视频发布失败: ${error.response?.data?.error?.message || error.m
- 上传视频失败: ${error.response?.data?.error?.message || error.mess
- 初始化响应缺少 publish_id 或 upload_url
- 初始化失败,缺少必要的上传参数
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/e23f757f051ac272.
Report an issue: GitHub.