screenpipe/screenpipe · error

skill files cannot contain NUL bytes: {}

Error message

skill files cannot contain NUL bytes: {}

What it means

Every file in a skill bundle must be valid UTF-8 text (this is enforced by `String::from_utf8` right before this check) and must not contain NUL bytes ('\0'). Binary content or text with embedded NULs aborts the bundle with this error. This guarantees bundle files are safe, printable text.

Source

Thrown at crates/screenpipe-engine/src/cli/team_skills.rs:325

        if !metadata.is_file() {
            anyhow::bail!("skill bundle contains a non-file: {}", path.display());
        }
        if files.len() >= MAX_FILES {
            anyhow::bail!("skill bundle has too many files (max {MAX_FILES})");
        }
        let bytes = std::fs::read(&path)?;
        if bytes.len() > MAX_FILE_BYTES {
            anyhow::bail!("skill file is too large: {}", path.display());
        }
        *total_bytes += bytes.len();
        if *total_bytes > MAX_TOTAL_BYTES {
            anyhow::bail!("skill bundle is too large (max {MAX_TOTAL_BYTES} bytes)");
        }
        let content = String::from_utf8(bytes)
            .with_context(|| format!("skill files must be UTF-8 text: {}", path.display()))?
            .replace("\r\n", "\n");
        if content.contains('\0') {
            anyhow::bail!("skill files cannot contain NUL bytes: {}", path.display());
        }
        let relative = path
            .strip_prefix(root)?
            .to_string_lossy()
            .replace('\\', "/");
        if relative.split('/').count() > MAX_DEPTH {
            anyhow::bail!("skill file path is nested too deeply: {relative}");
        }
        files.push((relative, content));
    }
    Ok(())
}

fn parse_frontmatter(raw: &str) -> anyhow::Result<(String, String)> {
    let normalized = raw.replace("\r\n", "\n");
    let rest = normalized
        .strip_prefix("---\n")
        .ok_or_else(|| anyhow::anyhow!("SKILL.md needs YAML frontmatter"))?;

View on GitHub (pinned to 4ebf712990)

Solutions

  1. Remove binary files from the skill directory — only UTF-8 text files are allowed
  2. Re-save any UTF-16 encoded file as UTF-8 (e.g. in the editor's save-as dialog, or `iconv -f UTF-16 -t UTF-8`)
  3. Check for corrupted files that contain NUL bytes (grep -P '\x00' -rl .) and regenerate them
  4. Move binary assets outside the bundle and reference them by path or URL in the skill instructions

Example fix

// before: notes.md saved as UTF-16 (contains NUL bytes)
// after
iconv -f UTF-16 -t UTF-8 notes.md > notes-utf8.md && mv notes-utf8.md notes.md
Defensive patterns

Strategy: validation

Validate before calling

fn assert_utf8_no_nul(root: &std::path::Path) -> anyhow::Result<()> {
    for entry in walkdir::WalkDir::new(root).into_iter().filter_map(Result::ok) {
        if entry.file_type().is_file() {
            let bytes = std::fs::read(entry.path())?;
            String::from_utf8(bytes).map_err(|_| {
                anyhow::anyhow!("non-UTF-8 file: {}", entry.path().display())
            })?;
            let text = std::fs::read_to_string(entry.path())?;
            if text.contains('\0') {
                anyhow::bail!("NUL byte in: {}", entry.path().display());
            }
        }
    }
    Ok(())
}

Type guard

fn is_clean_utf8(bytes: &[u8]) -> bool {
    std::str::from_utf8(bytes).map(|s| !s.contains('\0')).unwrap_or(false)
}

Try / catch

match prepare_bundle(&skill_dir) {
    Err(e) if e.to_string().contains("NUL") || e.to_string().contains("UTF-8") => {
        eprintln!("remove binaries and re-save files as UTF-8: {e}");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Bundling a skill directory that includes a binary file (image, compiled object, .png, .woff) or a text file corrupted with embedded NUL bytes (e.g. UTF-16 files whose byte pairs look like NUL-padded ASCII).

Common situations: Accidentally placing images or downloaded binaries in the skill folder; saving SKILL.md or scripts in UTF-16 encoding from some Windows editors; git-lfs placeholder files; log files with binary garbage.

Related errors


AI-assisted analysis of screenpipe/screenpipe@4ebf712990 (2026-09-01). Data as JSON: /api/errors/3f1664d2f84b34e9. Report an issue: GitHub.