{"record":{"id":"00ee6131367a84ea","repo":"nexu-io/open-design","slug":"conflict","errorCode":"CONFLICT","errorMessage":"finalize is already in progress for project ${projectId}","messagePattern":"finalize is already in progress for project (.+?)","errorType":"exception","errorClass":"FinalizePackageLockedError","httpStatus":409,"severity":"error","filePath":"apps/daemon/src/design/finalize-design.ts","lineNumber":304,"sourceCode":"  // For native projects, it lazily creates `.od/projects/<id>`.\n  fs.mkdirSync(dir, { recursive: true });\n  const finalPath = path.join(dir, OUTPUT_FILENAME);\n  const lockPath = path.join(dir, LOCK_FILENAME);\n  const tmpPath = path.join(\n    dir,\n    `${OUTPUT_FILENAME}.tmp.${process.pid}.${randomBytes(4).toString('hex')}`,\n  );\n  const now = options.now ?? (() => new Date());\n  const protocol = options.protocol ?? 'anthropic';\n  const baseUrl = options.baseUrl ?? defaultBaseUrlForFinalizeProtocol(protocol);\n  const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;\n\n  let lockFd: number | null = null;\n  try {\n    lockFd = fs.openSync(lockPath, 'wx');\n  } catch (err: unknown) {\n    if ((err as NodeJS.ErrnoException)?.code === 'EEXIST') {\n      throw new FinalizePackageLockedError(\n        `finalize is already in progress for project ${projectId}`,\n      );\n    }\n    throw err;\n  }\n\n  try {\n    // Phase 3: export transcript via the PR #493 primitive. Returns the\n    // disk path; we read the body and run it through the truncation\n    // policy so a 4 MB transcript does not blow Anthropic's context.\n    const transcriptResult = exportProjectTranscript(db, projectsRoot, projectId, { now });\n    const transcriptJsonl = fs.readFileSync(transcriptResult.path, 'utf8');\n    const truncatedJsonl = truncateTranscriptForPrompt(transcriptJsonl);\n\n    // Phase 4: design system. Project may not have one selected; readDesignSystem\n    // returns null on missing DESIGN.md so the prompt's design-system section\n    // gracefully falls back to \"(no design system selected for this project)\".\n    const designSystemId =","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/design/finalize-design.ts#L286-L322","documentation":"FinalizePackageLockedError: fs.openSync(lockPath, 'wx') failed with EEXIST. A `.finalize.lock` file already exists in the project directory, meaning another finalize is running, or a previous run crashed without cleaning up the lock. The route maps this to 409 CONFLICT.","triggerScenarios":"Two concurrent finalize requests for the same project (e.g. double-clicking Finalize, two browser tabs), or a prior finalize that crashed (OOM, kill, daemon restart) leaving a stale `.finalize.lock` behind.","commonSituations":"Double-clicking the Finalize button; two tabs finalizing the same project; the daemon was killed mid-finalize; an OOM or panic aborted the process before the finally block unlinked the lock.","solutions":["Wait for the in-progress finalize to complete, then retry.","If no finalize is running (confirm via process list / a second request still returning 409), remove the stale lock: `rm <projectDir>/.finalize.lock`.","Avoid triggering concurrent finalize on the same project from multiple tabs."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Optional pre-check: only helps if no concurrent finalize is known.\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nconst lockPath = join(projectDir, '.finalize.lock');\nif (existsSync(lockPath)) {\n  // Either wait or prompt the operator to clear a stale lock.\n}","typeGuard":"import { FinalizePackageLockedError } from './finalize-design.js';\nfunction isFinalizeLocked(err: unknown): err is FinalizePackageLockedError {\n  return err instanceof FinalizePackageLockedError;\n}","tryCatchPattern":"import { FinalizePackageLockedError } from './finalize-design.js';\ntry {\n  await finalizeDesignPackage(db, projectsRoot, dsRoot, projectId, options);\n} catch (err) {\n  if (err instanceof FinalizePackageLockedError) {\n    // HTTP route maps to 409 CONFLICT; retry after the in-progress run,\n    // or `rm <projectDir>/.finalize.lock` if it is stale.\n    return res.status(409).json({ code: 'CONFLICT', error: err.message });\n  }\n  throw err;\n}","preventionTips":["Avoid triggering concurrent finalize on the same project (debounce the UI button).","After a crash, clear stale `.finalize.lock` before retrying.","Treat repeated 409s with no running finalize as a stale-lock condition."],"tags":["finalize","concurrency","lockfile","conflict"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}