yikart/AiToEarn · error · BadRequestException
初始化视频发布失败: ${error.response?.data?.error?.message || error.m
Error message
初始化视频发布失败: ${error.response?.data?.error?.message || error.message} What it means
This BadRequestException is the catch-all rethrow of TikTokService.initVideoPublish: any exception raised while calling POST ${apiBaseUrl}/v2/post/publish/video/init/ — including the missing publish_id/upload_url guard (error 421) and any axios/TikTok API error — is caught, logged, and rethrown as BadRequestException('初始化视频发布失败: <TikTok error.message or axios message>'). It deliberately overwrites the original error's identity, so the missing-field BadRequestException from line 224 also emerges with this wrapper text.
Source
Thrown at project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.service.ts:231
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,
videoBuffer: Buffer,
initData?: any
): Promise<any> {
try {
// 如果没有提供初始化数据,先进行初始化
if (!initData) {
// 计算视频大小并进行初始化View on GitHub (pinned to d3aa8bea5b)
Solutions
- Read the logged detail ('初始化TikTok视频发布失败:' + error.response?.data) to distinguish an HTTP error from the local missing-field guard failure.
- If the message says 缺少publish_id或upload_url, inspect the raw TikTok response for an error envelope returned with HTTP 200 (see solutions for error 421).
- If it's a TikTok API error, fix per its code: refresh the access token, request/verify video.publish scope, or correct post_info/source_info fields.
- Validate inputs before the call: videoSize > 0, chunkSize within 5–64MB, title length within TikTok's limit, privacy_level valid for app status.
- Stop double-wrapping: rethrow the inner BadRequestException as-is so the specific cause ('缺少publish_id或upload_url') is not masked.
Example fix
// before: every failure becomes the same generic message
} catch (error) {
this.logger.error('初始化TikTok视频发布失败:', error.response?.data || error.message);
throw new BadRequestException(`初始化视频发布失败: ${error.response?.data?.error?.message || error.message}`);
}
// after: preserve already-typed errors, wrap only upstream failures
} catch (error) {
this.logger.error('初始化TikTok视频发布失败:', error.response?.data || error.message);
if (error instanceof BadRequestException) throw error;
const apiErr = error.response?.data?.error;
throw new BadRequestException(`初始化视频发布失败: [${apiErr?.code ?? 'UNKNOWN'}] ${apiErr?.message ?? error.message}`);
} Defensive patterns
Strategy: try-catch
Validate before calling
function validatePublishInputs(accessToken: string, videoSize: number, videoInfo: { privacyStatus?: string; title?: string; tags?: string[] }) {
if (!accessToken) throw new Error('缺少 accessToken');
if (!Number.isFinite(videoSize) || videoSize <= 0) throw new Error('videoSize 非法');
const privacy = videoInfo.privacyStatus ?? 'PUBLIC';
const allowed = ['PUBLIC', 'SELF_ONLY', 'FRIENDS'];
if (!allowed.includes(privacy)) throw new Error(`非法 privacy_level: ${privacy}`);
const title = `${videoInfo.title ?? ''} ${(videoInfo.tags ?? []).map(t => '#' + t.replace(/^#/, '')).join(' ')}`;
if (title.length > 2200) throw new Error('标题(含 hashtag)超出 TikTok 长度限制');
} Type guard
function isAxiosUpstreamError(e: unknown): e is { isAxiosError: true; response?: { status: number; data: { error?: { code: string; message: string } } }; message: string } {
return typeof e === 'object' && e !== null && (e as any).isAxiosError === true;
} Try / catch
try {
const initData = await tiktokService.initVideoPublish(accessToken, videoSize, videoInfo);
} catch (e) {
if (e instanceof BadRequestException && e.message.includes('缺少publish_id')) {
// local response-shape failure — inspect raw response, do not blindly retry
} else if (isAxiosUpstreamError(e) && e.response?.status === 401) {
// token invalid — refresh and retry once
} else if (isAxiosUpstreamError(e) && !e.response) {
// network error — retry with backoff
}
throw e;
} Prevention
- Validate accessToken, videoSize, privacy_level and title length before calling initVideoPublish.
- Refresh tokens before long publish pipelines; 401 mid-flow means re-auth, not retry.
- Rethrow typed/local errors unchanged so specific causes are not masked by generic wrappers.
- Record TikTok error.code from error.response.data.error in structured logs/alerts.
- Verify app scope (video.publish) and production audit status before enabling direct publish.
When it happens
Trigger: initVideoPublish's try block fails for any reason: (1) axios error from firstValueFrom (4xx/5xx from TikTok: invalid token, missing video.publish scope, invalid post_info/source_info values, file size outside limits); (2) the explicit '缺少publish_id或upload_url' BadRequestException thrown at line 224; (3) network failure where error.response is undefined so error.message (e.g. 'timeout of 10000ms exceeded') is interpolated.
Common situations: Developers hit this with expired/revoked OAuth tokens, apps whose video.publish scope isn't audit-approved, privacy_level values not permitted for the app tier, video_size/chunk_size/total_chunk_count inconsistencies in source_info, oversized title strings with hashtags exceeding TikTok limits, and confusingly also when the response genuinely lacks publish_id/upload_url because the inner guard's message gets swallowed by this wrapper.
Related errors
- 初始化视频上传失败: ${error.response?.data?.error?.message || error.m
- 上传视频失败: ${error.response?.data?.error?.message || error.mess
- 获取视频列表失败: ${error.response?.data?.error?.message || error.me
- 获取视频详情失败: ${error.response?.data?.error?.message || error.me
- 初始化视频发布失败,缺少publish_id或upload_url
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/8ab9a4b25de79be5.
Report an issue: GitHub.