{"record":{"id":"563aa2d43a0b71fe","repo":"thedotmack/claude-mem","slug":"conflict","errorCode":"Conflict","errorMessage":"Generation job is currently processing; cancel or wait for completion before retrying","messagePattern":"Generation job is currently processing; cancel or wait for completion before retrying","errorType":"http","errorClass":null,"httpStatus":409,"severity":"warning","filePath":"src/server/routes/v1/ServerV1PostgresRoutes.ts","lineNumber":1463,"sourceCode":"      load: (projectId) => repo.getByIdForScope({ id, projectId, teamId }),\n    });\n    if (!current) return null;\n\n    // Idempotent fast-path: already queued -> emit audit only, no DB writes.\n    if (current.status === 'queued') {\n      await this.auditWrite(req, 'generation_job.retried_by_operator', current.id, current.projectId, {\n        outcome: 'noop_already_queued',\n        currentAttempts: current.attempts,\n        requestId: req.requestId ?? null,\n      });\n      return { job: current, retriedCount: extractRetriedCount(current.payload), alreadyQueued: true };\n    }\n\n    if (current.status === 'processing') {\n      // Refuse retry on in-flight jobs — the running worker MUST be allowed\n      // to finish or fail through its normal lifecycle. Operator can wait\n      // or cancel, then retry.\n      res.status(409).json({\n        error: 'Conflict',\n        message: 'Generation job is currently processing; cancel or wait for completion before retrying',\n      });\n      return null;\n    }\n\n    if (current.status === 'completed') {\n      // Refuse retry on already-completed jobs. The deduplication index on\n      // observations (generation_key = job_id + index + content) does NOT\n      // protect against re-running the provider, because LLM output is\n      // non-deterministic and the second run almost always produces a\n      // different content string. Replaying would persist a parallel set\n      // of observations attributed to the same generation_job_id.\n      // cancelGenerationJob applies the same 409 guard for the same reason.\n      res.status(409).json({\n        error: 'Conflict',\n        message: 'Generation job already completed; retrying would duplicate observations',\n      });","sourceCodeStart":1445,"sourceCodeEnd":1481,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/server/routes/v1/ServerV1PostgresRoutes.ts#L1445-L1481","documentation":"POST /v1/jobs/:id/retry answers 409 when the job's current status is 'processing'. An in-flight worker must be allowed to finish or fail through its normal lifecycle, so re-enqueueing is refused; the operator can wait for completion or cancel first. This is a state-machine guard, not an error in the request itself.","triggerScenarios":"Calling retry while the generation worker holds the job — e.g. right after a failed attempt bounced it back to queued and it was picked up again, or while a dashboard shows status 'processing'.","commonSituations":"Retry buttons that don't refresh job status first; automation polling faster than the worker lifecycle; double-clicking retry.","solutions":["GET /v1/jobs/:id and check status is not 'processing' before retrying","Wait for the worker to settle, or POST /v1/jobs/:id/cancel and then retry","Disable the retry action in UI/automation while status is 'processing'"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Check status before retrying\nconst res = await fetch(`${base}/v1/jobs/${jobId}`, { headers });\nconst { generationJob } = await res.json();\nif (generationJob.status === 'processing') {\n  // wait, or cancel first — do not retry\n  return { deferred: true };\n}\nawait fetch(`${base}/v1/jobs/${jobId}/retry`, { method: 'POST', headers });","typeGuard":"type RetryableStatus = 'failed' | 'cancelled';\nconst isRetryable = (status: string): status is RetryableStatus =>\n  status === 'failed' || status === 'cancelled';","tryCatchPattern":"Treat 409 'currently processing' as a signal to back off: re-check status on a timer and retry only once the job leaves 'processing'; never blind-retry the POST.","preventionTips":["Always render job status next to the retry action and gate the action on it","Debounce retry buttons to kill double-clicks","In automation, poll GET /v1/jobs/:id to terminal status before deciding to retry"],"tags":["http-409","jobs","retry","state-machine","concurrency"],"backgroundTag":"invalid-state-transition","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}