yikart/AiToEarn · error · CustomErrorOrNull

ERR_CODE_OR_NULL

ERR_CODE_OR_NULL

Error message

ChannelPublishQueueRemoveFailed

What it means

cancelTask calls queueService.removeJob(taskId) when the record is in PublishStatus.Queued. If the queue reports the job could not be removed (removed falsy) it throws ChannelPublishQueueRemoveFailed — the pending job stayed or disappeared in an unexpected way.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/publish/tasks/publish-task.service.ts:408

        : { category: PlatformErrorCategory.Unknown, retryable: false }
      await this.stateService.markUpdatedFailed(taskId, {
        category: error.category,
        code: error.code,
        message: this.getErrorMessage(err, ResponseCode.PublishTaskUpdateFailed),
        originalData: error.originalData,
        retryable: error.retryable,
        occurredAt: new Date(),
      })
    }
  }

  async cancelTask(userId: string, taskId: string): Promise<void> {
    const record = await this.getTaskForUser(taskId, userId)

    if (record.status === PublishStatus.Queued) {
      const removed = await this.queueService.removeJob(taskId)
      if (!removed) {
        throw new AppException(ResponseCode.ChannelPublishQueueRemoveFailed)
      }
    }

    if (record.status === PublishStatus.PlatformScheduled && record.platformWorkId) {
      const platformWorkId = record.platformWorkId
      const { provider } = this.getPublishRuntime(record.accountType)
      if (provider.cancel) {
        const account = await this.getRecordAccount(record)
        const result = await this.runWithCredentialRefresh(account, record.userId, credential => provider.cancel!({
          taskId,
          platform: record.accountType,
          platformWorkId,
          credential: this.toPublishCredential(credential, account),
        }))
        if (!result.canceled) {
          throw new AppException(ResponseCode.ChannelPublishPlatformCancelFailed)
        }
      }

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Refresh the record status and retry the cancel — the task likely already left Queued
  2. Check queue (Redis) connectivity and worker activity
  3. If the task is already running, wait for completion and then cancel via the platform path if supported

Example fix

// before
await taskService.cancelTask(userId, taskId) // status snapshot said Queued
// after
const fresh = await taskService.getTaskForUser(taskId, userId)
if (fresh.status === PublishStatus.Queued) {
  await taskService.cancelTask(userId, taskId)
} else { /* handle running/terminal state */ }
Defensive patterns

Strategy: try-catch

Validate before calling

const fresh = await taskService.getTaskForUser(taskId, userId)
if (fresh.status !== 'queued') throw new Error('task no longer queued; cancel path differs')

Try / catch

try {
  await taskService.cancelTask(userId, taskId)
} catch (e) {
  if (e.code === 'ChannelPublishQueueRemoveFailed') {
    // refresh status; job likely already active — retry or wait for completion
  } else throw e
}

Prevention

When it happens

Trigger: Cancelling a Queued task whose BullMQ job cannot be removed — typically because the job already started/was picked up by a worker (status is stale) or the queue backend is unavailable.

Common situations: Race between the status snapshot and actual queue state (job moved to active just before cancel); Redis connectivity problems; job already drained but the DB status not yet updated.

Related errors


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