yikart/AiToEarn · error · BilibiliPlatformException
ChannelPlatformMediaProcessingFailed
ChannelPlatformMediaProcessingFailed
Error message
ResponseCode.ChannelPlatformMediaProcessingFailed
What it means
Bilibili's open-platform cover upload endpoint (POST /arcopen/fn/archive/cover/upload) completed at the HTTP level, but the response body contained no `data.url`. The service treats a cover upload without a returned URL as a platform-side media processing failure and throws ChannelPlatformMediaProcessingFailed, blaming the Bilibili platform (PlatformErrorCauseType.Platform) rather than the caller.
Source
Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/bilibili/bilibili.service.ts:354
async uploadCover(
accessToken: string,
coverUrl: string,
): Promise<string> {
const coverBuffer = await this.mediaService.getBuffer({
platform: AccountType.Bilibili,
endpoint: 'downloadCover',
url: coverUrl,
})
const formData = new FormData()
formData.append('file', new Blob([new Uint8Array(coverBuffer)]), this.resolveFileName(coverUrl, 'cover.jpg'))
const response = await this.platformHttp.post<BilibiliApiResponse<{ url: string }>>(`${this.openBaseUrl}/arcopen/fn/archive/cover/upload`, formData, {
headers: this.buildSignedHeaders(accessToken, '', 'multipart/form-data'),
})
if (!response.data.data?.url) {
throw new BilibiliPlatformException({
code: ResponseCode.ChannelPlatformMediaProcessingFailed,
category: PlatformErrorCategory.MediaProcessingFailed,
context: { endpoint: 'POST /arcopen/fn/archive/cover/upload' },
cause: { type: PlatformErrorCauseType.Platform },
})
}
return response.data.data.url
}
async getArchiveDetail(
accessToken: string,
bvid: string,
): Promise<BilibiliArchiveDetail> {
const data = await this.signedRequest<{
resource_id: string
title: string
desc: stringView on GitHub (pinned to d3aa8bea5b)
Solutions
- Log the full response body of the cover upload call to see Bilibili's in-body error code/message (a 200 status can still carry an error payload).
- Verify the cover file is a supported format (jpg/png) and within Bilibili's size limits before uploading.
- Check the access token is valid and has archive-write scope; refresh it if expired and retry the upload.
- Compare the actual response shape against this handler — if Bilibili moved the `url` field, update the extraction in bilibili.service.ts.
Example fix
// before
const response = await this.platformHttp.post<BilibiliApiResponse<{ url: string }>>(`${this.openBaseUrl}/arcopen/fn/archive/cover/upload`, formData, { headers: this.buildSignedHeaders(accessToken, '', 'multipart/form-data') })
if (!response.data.data?.url) {
throw new BilibiliPlatformException({ code: ResponseCode.ChannelPlatformMediaProcessingFailed, context: { endpoint: 'POST /arcopen/fn/archive/cover/upload' }, cause: { type: PlatformErrorCauseType.Platform } })
}
// after
const response = await this.platformHttp.post<BilibiliApiResponse<{ url: string }>>(`${this.openBaseUrl}/arcopen/fn/archive/cover/upload`, formData, { headers: this.buildSignedHeaders(accessToken, '', 'multipart/form-data') })
this.logger.warn({ body: response.data }, 'bilibili cover upload raw response') // surface in-body error code
if (!response.data.data?.url) {
throw new BilibiliPlatformException({ code: ResponseCode.ChannelPlatformMediaProcessingFailed, context: { endpoint: 'POST /arcopen/fn/archive/cover/upload', body: response.data }, cause: { type: PlatformErrorCauseType.Platform } })
} Defensive patterns
Strategy: validation
Validate before calling
// pre-flight checks before cover upload
function validateCoverForBilibili(file: { mimetype: string; size: number }): string | null {
const allowed = ['image/jpeg', 'image/png']
if (!allowed.includes(file.mimetype)) return `unsupported format ${file.mimetype}; use jpg/png`
if (file.size > 5 * 1024 * 1024) return 'cover exceeds Bilibili size limit'
return null
} Type guard
function hasUploadedCoverUrl(res: BilibiliApiResponse<{ url: string }>): res is BilibiliApiResponse<{ url: string }> & { data: { data: { url: string } } } {
return typeof res?.data?.url === 'string' && res.data.url.length > 0
} Try / catch
try {
const coverUrl = await channels.cover(channelId, coverFile)
} catch (e) {
if (e.code === 'ChannelPlatformMediaProcessingFailed') {
logger.warn('bilibili rejected cover upload; check image format/size and token, then retry')
// fall back to default cover or re-queue upload
} else throw e
} Prevention
- Validate cover format and size client-side before upload.
- Keep channel access tokens refreshed before publish flows.
- Log raw platform responses to catch in-body errors hidden behind HTTP 200.
- Pin/monitor the Bilibili open API version your parsing assumes.
When it happens
Trigger: Calling cover/uploadCover for a bilibili channel when the signed multipart POST succeeds but the JSON body is missing `data.data.url` — e.g. Bilibili returned an error code inside a 200 response, rejected the image silently, or changed the response shape.
Common situations: Cover image in an unsupported format/size (not jpg/png, oversized), expired or insufficiently scoped access_token causing an in-body error, Bilibili open API degradation, or an API version change that renames/moves the `url` field.
Related errors
- Relay API error [${body.code}]: ${body.message}
- ChannelAuthRefreshTokenMissing
- ChannelPlatformOperationNotSupported
- InvalidWorkLink
- ChannelAccessTokenFailed
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/058dfe71645206a5.
Report an issue: GitHub.