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
- Refresh the record status and retry the cancel — the task likely already left Queued
- Check queue (Redis) connectivity and worker activity
- 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
- Re-read task status immediately before canceling
- Verify Redis/queue health before cancel operations
- Avoid canceling tasks at the exact moment they are being dispatched
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.