BoundaryML/baml · error · io::Error

profiling store contains a symlink

Error message

profiling store contains a symlink

What it means

During a store integrity/usage scan, the recursive `scan` walker inspects each entry's file_type and rejects any symlink outright with InvalidData 'profiling store contains a symlink'. The store treats symlinks as a security and integrity hazard (they could redirect reads/writes outside the store root), so a symlink anywhere under the root fails the scan.

Source

Thrown at baml_language/crates/bex_prof_store/src/prof/backend/store.rs:1619

        .truncate(false)
        .open(path)
}

fn write_synced_file(path: &Path, bytes: &[u8], platform: &dyn StorePlatform) -> io::Result<()> {
    let mut file = OpenOptions::new().write(true).create_new(true).open(path)?;
    file.write_all(bytes)?;
    file.flush()?;
    platform.sync_file(&file)
}

fn scan_physical_usage(root: &Path) -> io::Result<u64> {
    fn scan(directory: &Path, root: &Path, total: &mut u64) -> io::Result<()> {
        for entry in fs::read_dir(directory)? {
            let entry = entry?;
            let path = entry.path();
            let file_type = entry.file_type()?;
            if file_type.is_symlink() {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "profiling store contains a symlink",
                ));
            }
            if file_type.is_dir() {
                scan(&path, root, total)?;
            } else if file_type.is_file() {
                let relative = path.strip_prefix(root).map_err(io::Error::other)?;
                if relative == Path::new("publish.lock")
                    || relative == Path::new("usage.state")
                    || relative == Path::new("tmp/usage-state.pending")
                {
                    continue;
                }
                *total = total
                    .checked_add(entry.metadata()?.len())
                    .ok_or_else(|| io::Error::other("profiling usage overflow"))?;
            } else {

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Find and remove the symlink: `find <store-root> -type l` then delete it and, if needed, copy the real target into place with `cp -L`.
  2. Restore the directory entry as a regular file (replace the symlink with a copy of its target).
  3. Reconfigure any sync/backup tooling to not create symlinks inside the profiling store directory.
  4. Point the profiling store at a dedicated directory (e.g. under your app's data dir) not managed by symlink-producing tooling.

Example fix

// shell
// before: segment -> /elsewhere/segment.bin (symlink)
// after
cp -L /elsewhere/segment.bin segment.tmp && mv segment.tmp segment && find <store-root> -type l  # expect no output
Defensive patterns

Strategy: validation

Validate before calling

fn store_has_symlinks(root: &Path) -> io::Result<bool> {
    for entry in walkdir(root) {
        if entry.file_type().is_symlink() { return Ok(true); }
    }
    Ok(false)
}
// run before opening the store; if true, clean the directory first

Try / catch

match scan_store(&root) {
    Err(e) if e.kind() == io::ErrorKind::InvalidData && e.to_string().contains("symlink") => {
        replace_symlinks_with_copies(&root)?;
        scan_store(&root)
    }
    other => other,
}

Prevention

When it happens

Trigger: Running the store scan/size-accounting routine when any entry under the profiling store directory (at any depth) is a symbolic link, as reported by entry.file_type().is_symlink().

Common situations: A user replaced a segment or shard file with a symlink to save space or point at another location; build/backup tooling created symlinks inside the data directory; syncing tools (e.g. dropbox, git) materialized symlinks in the store path.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/bc98775befb5e3b0. Report an issue: GitHub.