{"record":{"id":"e70ef5202f0fad57","repo":"Yeachan-Heo/oh-my-codex","slug":"native-assets-publication-lock-timeout-path","errorCode":null,"errorMessage":"[native-assets] publication-lock-timeout: ${path}; elapsed=${Math.round(performance.now() - started)}ms deadline=${lockWaitMs(env)}ms; ${diagnostic.classification}${owner}. Confirm no OMX hydration process is active for this cache key, remove only this named lock manually, then retry.","messagePattern":"\\[native-assets\\] publication-lock-timeout: (.+?); elapsed=(.+?)ms deadline=(.+?)ms; (.+?)(.+?)\\. Confirm no OMX hydration process is active for this cache key, remove only this named lock manually, then retry\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/native-assets.ts","lineNumber":591,"sourceCode":"      try {\n        const { bytesWritten } = await handle.write(recordBytes, 0, recordBytes.length, 0);\n        if (bytesWritten !== recordBytes.length) throw new Error('[native-assets] incomplete lock write');\n        const readback = Buffer.alloc(recordBytes.length);\n        const { bytesRead } = await handle.read(readback, 0, readback.length, 0);\n        if (bytesRead !== readback.length || !readback.equals(recordBytes)) throw new Error('[native-assets] lock readback mismatch');\n        const identity = await handle.stat();\n        const fileIdentity = { dev: identity.dev, ino: identity.ino, size: identity.size };\n        if (!identity.isFile() || identity.nlink !== 1) throw new Error('[native-assets] unsafe publication lock');\n        await reInspectPath(path, fileIdentity, true);\n        return { path, token, record, identity: fileIdentity };\n\n      } finally { await handle.close(); }\n    } catch (error) {\n      if (errno(error) !== 'EEXIST') throw error;\n      if (performance.now() >= deadline) {\n        const diagnostic = await inspectLock(path, binaryPath) ?? { path, classification: 'metadata-unavailable' as const };\n        const owner = diagnostic.owner ? ` owner=${JSON.stringify(diagnostic.owner)}` : '';\n        throw new Error(`[native-assets] publication-lock-timeout: ${path}; elapsed=${Math.round(performance.now() - started)}ms deadline=${lockWaitMs(env)}ms; ${diagnostic.classification}${owner}. Confirm no OMX hydration process is active for this cache key, remove only this named lock manually, then retry.`);\n      }\n\n      await new Promise<void>((done) => setTimeout(done, LOCK_RETRY_MS));\n    }\n  }\n}\n\nasync function releaseCacheLock(lock: PublicationLock): Promise<ManagedNativeBinaryInspection | undefined> {\n  try {\n    const reopened = await readOpenedFile(lock.path, false, false);\n    if (!sameFile(lock.identity, reopened) || reopened.text !== lock.record) return { state: 'cleanup-failed' };\n    const current = await lstat(lock.path);\n    if (!sameFile(lock.identity, { dev: current.dev, ino: current.ino, size: current.size }) || !current.isFile() || current.nlink !== 1) return { state: 'cleanup-failed' };\n    await unlink(lock.path);\n  } catch (error) { return absent(error) ? undefined : { state: 'cleanup-failed' }; }\n  return undefined;\n}\n","sourceCodeStart":573,"sourceCodeEnd":609,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/native-assets.ts#L573-L609","documentation":"The publication lock for a cache destination could not be acquired within the wait deadline (another holder kept it, creation raced with EEXIST until the timeout elapsed). The message includes the lock path, elapsed time, deadline, an inspected classification, and owner info, plus remediation: verify no OMX hydration is active for that key, remove only that named lock, retry.","triggerScenarios":"acquireCacheLock when O_CREAT|O_EXCL returns EEXIST repeatedly past lockWaitMs — a concurrent hydration holding the lock, or a stale/crashed lock left behind after a SIGKILL or power loss.","commonSituations":"Parallel npm postinstall scripts hydrating the same cache key; a previously killed process leaving a stale lock; CI runners reusing a warm cache volume with leftover locks.","solutions":["Confirm no other hydration process is running for that cache key, then delete exactly the lock path printed in the message and retry.","Serialize installs (single postinstall) in CI to avoid lock contention.","If it recurs, raise the lock wait via the library's env knob or pre-hydrate caches in the image build."],"exampleFix":"# after confirming no hydration is active\nrm /path/from/error/message/.lock\nnpm run your-hydrate-step","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await hydrateNativeBinary(); } catch (e) {\n  const m = /publication-lock-timeout: ([^;]+)/.exec(String(e));\n  if (m) { /* confirm no active hydration, rm exactly m[1], retry once */ await rm(m[1], { force: true }); return hydrateNativeBinary(); }\n  throw e;\n}","preventionTips":["Never run concurrent installs hydrating the same cache key","Clean stale locks after SIGKILLed CI jobs","Pre-hydrate caches in image builds to avoid runtime locking"],"tags":["native-assets","lock","timeout","stale-lock","concurrency"],"backgroundTag":"file-lock-timeout","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}