vercel/turborepo · warning

error opening log file: {err:?}

Error message

error opening log file: {err:?}

What it means

replay_logs (crates/turborepo-ui/src/logs.rs:99) opens a previously written task log file to replay its output (typically after a cache hit or in watch mode). If File::open fails, turbo emits this warning and returns Error::CannotReadLogs(err); the stored log cannot be shown.

Source

Thrown at crates/turborepo-ui/src/logs.rs:100

        if let Some(log_file) = &mut self.log_file {
            log_file.flush()?;
        }
        if let Some(prefixed_writer) = &mut self.writer {
            prefixed_writer.flush()?;
        }

        Ok(())
    }
}

pub fn replay_logs<W: Write>(
    mut output: W,
    log_file_name: &AbsoluteSystemPath,
) -> Result<(), Error> {
    debug!("start replaying logs");

    let log_file = File::open(log_file_name).map_err(|err| {
        turborepo_log::warn(
            turborepo_log::Source::turbo(turborepo_log::Subsystem::Logs),
            format!("error opening log file: {err:?}"),
        )
        .emit();
        Error::CannotReadLogs(err)
    })?;

    let mut log_reader = BufReader::new(log_file);

    let mut buffer = Vec::new();
    loop {
        let num_bytes = log_reader
            .read_until(b'\n', &mut buffer)
            .map_err(Error::CannotReadLogs)?;
        if num_bytes == 0 {
            break;
        }

View on GitHub (pinned to f9245100cf)

Solutions

  1. If logs were pruned intentionally, clean the matching cache entry too so hits and logs stay consistent (remove the whole cache entry folder or run turbo clean).
  2. Check the file exists and is readable: ls -l on the reported path; fix ownership/permissions of the cache dir.
  3. Avoid sharing one cache directory between multiple concurrent machines/users; use per-user cache dirs or a remote cache.
  4. If it recurs, capture the {err:?} detail — ENOENT points to pruning races, EACCES to permissions.

Example fix

# before: cache hit references a pruned log file -> error opening log file: Os { code: 2 }
turbo run build --force=false

# after: evict the half-pruned entry so it is treated as a miss
turbo clean   # or: rm -rf .turbo/cache/<hash-of-the-task>
turbo run build
Defensive patterns

Strategy: fallback

Validate before calling

#!/usr/bin/env bash
# before replaying logs from a cache hit, confirm the log file exists
LOG="$1"
[ -r "$LOG" ] || { echo "log missing — treating as cache miss" >&2; exit 3; }
cat "$LOG"

Try / catch

// replay_logs returns Result<(), turborepo_ui::Error>
match replay_logs(&mut out, &log_path) {
    Ok(()) => {}
    Err(e) => {
        // degrade gracefully: show a placeholder instead of failing the task view
        eprintln!("(cached logs unavailable: {e:?})");
    }
}

Prevention

When it happens

Trigger: Calling replay_logs with a path that does not exist (ENOENT — the log was deleted or never written), is not readable (EACCES), is a directory, or is locked by another process on Windows.

Common situations: A cache entry survives but its companion log file was pruned (turbo clean / cache eviction / manual deletion of files inside the cache dir), cache directory copied between machines with lost permissions, or concurrent runs racing over the same cache directory.

Related errors


AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17). Data as JSON: /api/errors/2eb24b8db9f56f35. Report an issue: GitHub.