Yeachan-Heo/oh-my-codex · error · Error

[native-assets] incomplete lock write

Error message

[native-assets] incomplete lock write

What it means

While creating the publication lock file, a write to the freshly O_EXCL-created file reported fewer bytes than the lock record. This indicates a filesystem-level short write (disk full, quota, or exotic FS), so the lock would be corrupt and is rejected immediately.

Source

Thrown at src/cli/native-assets.ts:575

  return { state: 'verified', path: effectiveBinaryPath!, lock: lockDiagnostic };
}

async function acquireCacheLock(binaryPath: string, env: NodeJS.ProcessEnv): Promise<PublicationLock> {

  const path = lockPath(binaryPath);
  const started = performance.now();
  const deadline = started + lockWaitMs(env);

  for (;;) {
    const token = uuid();
    const record = lockRecord(token, binaryPath);

    const recordBytes = Buffer.from(record, 'utf8');
    try {
      const handle = await open(path, constants.O_RDWR | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
      try {
        const { bytesWritten } = await handle.write(recordBytes, 0, recordBytes.length, 0);
        if (bytesWritten !== recordBytes.length) throw new Error('[native-assets] incomplete lock write');
        const readback = Buffer.alloc(recordBytes.length);
        const { bytesRead } = await handle.read(readback, 0, readback.length, 0);
        if (bytesRead !== readback.length || !readback.equals(recordBytes)) throw new Error('[native-assets] lock readback mismatch');
        const identity = await handle.stat();
        const fileIdentity = { dev: identity.dev, ino: identity.ino, size: identity.size };
        if (!identity.isFile() || identity.nlink !== 1) throw new Error('[native-assets] unsafe publication lock');
        await reInspectPath(path, fileIdentity, true);
        return { path, token, record, identity: fileIdentity };

      } finally { await handle.close(); }
    } catch (error) {
      if (errno(error) !== 'EEXIST') throw error;
      if (performance.now() >= deadline) {
        const diagnostic = await inspectLock(path, binaryPath) ?? { path, classification: 'metadata-unavailable' as const };
        const owner = diagnostic.owner ? ` owner=${JSON.stringify(diagnostic.owner)}` : '';
        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.`);
      }

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Free space on the volume hosting the cache root.
  2. Point the cache root env var at a local, non-network filesystem.
  3. Re-run hydration once space is available.
Defensive patterns

Strategy: try-catch

Try / catch

try { await hydrateNativeBinary(); } catch (e) { if (/incomplete lock write/.test(String(e))) { /* free disk space, retry */ } throw e; }

Prevention

When it happens

Trigger: acquireCacheLock when handle.write of the lock record returns bytesWritten < record length — ENOSPC conditions, full tmpfs, or flaky network filesystems backing the cache.

Common situations: Docker containers with tiny tmpfs caches; disk-full CI runners; NFS-backed HOME directories.

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/15d96591c835cf3f. Report an issue: GitHub.