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.

Solutions

  1. 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).
  2. Verify the cover file is a supported format (jpg/png) and within Bilibili's size limits before uploading.
  3. Check the access token is valid and has archive-write scope; refresh it if expired and retry the upload.
  4. 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

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


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/058dfe71645206a5. Report an issue: GitHub.

Appendix: 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: string

View on GitHub (pinned to d3aa8bea5b)