BigPizzaV3/CodexPlusPlus · warning
zip entry has empty path
Error message
zip entry has empty path
What it means
safe_zip_path (crates/codex-plus-core/src/plugin_marketplace.rs:453) rejects entry names that normalize to an empty relative path — e.g. the name '.', an empty string, or a run of only-current-dir components — after the escape check passes. An empty path has no file to write and would otherwise collapse into writing onto the destination directory itself, so extraction refuses it.
Source
Thrown at crates/codex-plus-core/src/plugin_marketplace.rs:453
.with_context(|| format!("failed to read zip entry {}", file.name()))?;
std::fs::write(&output_path, contents)
.with_context(|| format!("failed to write {}", output_path.display()))?;
}
Ok(())
}
fn safe_zip_path(name: &str) -> anyhow::Result<PathBuf> {
let path = Path::new(name);
let mut relative = PathBuf::new();
for component in path.components() {
match component {
Component::Normal(value) => relative.push(value),
Component::CurDir => {}
_ => anyhow::bail!("zip entry escapes destination: {name}"),
}
}
if relative.as_os_str().is_empty() {
anyhow::bail!("zip entry has empty path");
}
Ok(relative)
}
fn zip_entry_relative_path(name: &str) -> Option<PathBuf> {
let path = Path::new(name);
let mut components = path.components();
match components.next()? {
Component::Normal(_) => {}
_ => return None,
}
let mut relative = PathBuf::new();
for component in components {
match component {
Component::Normal(value) => relative.push(value),
Component::CurDir => {}
_ => return None,
}View on GitHub (pinned to 1f431ae49b)
Solutions
- Rebuild the zip without degenerate entries: list with `unzip -l` and repack excluding '.'/empty names
- Re-download the artifact from the official URL — empty-name entries often indicate corruption in transit
- If writing the zip programmatically, skip directory markers and empty names when adding entries (zipfile: skip when arcname in ('', '.'))
- Treat repeated occurrences as tampering and fall back to the embedded marketplace copy
Example fix
# python: before — accidentally adds empty-name entry
zf.writestr('', b'')
# after — skip degenerate names entirely
if name in ('', '.'):
continue
zf.writestr(name, data) Defensive patterns
Strategy: validation
Validate before calling
fn zip_names_valid(names: impl IntoIterator<Item = String>) -> bool {
names.into_iter().all(|name| {
let mut rel = std::path::PathBuf::new();
for c in std::path::Path::new(&name).components() {
match c {
std::path::Component::Normal(v) => rel.push(v),
std::path::Component::CurDir => {}
_ => return false,
}
}
!rel.as_os_str().is_empty()
})
} Type guard
fn entry_name_usable(name: &str) -> bool {
!name.trim().is_empty() && name != "."
} Try / catch
// Skip-and-continue for degenerate entries is acceptable when repacking your own artifacts;
// for downloads, fail the install
Err(e) if e.to_string() == "zip entry has empty path" => {
tracing::warn!("archive contains empty-name entry; treating artifact as malformed");
redownload_and_retry_once().await?
} Prevention
- When generating zips programmatically, skip '' and '.' entry names
- Validate archives with `unzip -t` (and a name scan) before shipping or installing
- Re-download on malformed-archive errors — empty names usually indicate corruption
When it happens
Trigger: Extracting a marketplace zip containing a directory entry named '.' or '' (or a sequence of '.' components) — typically produced by buggy archivers, hand-rolled zip writers, or deliberate malformed-archive fuzzing.
Common situations: Zips packed with a redundant './' prefix entry; archives created by scripts using zipfile with an accidental empty arcname; corrupted downloads where entry headers degrade to empty names.
Related errors
- zip entry escapes destination: {name}
- openai/plugins marketplace download is too large: {} bytes
- downloaded openai/plugins marketplace is invalid
- downloaded openai/plugins marketplace root mismatch
- embedded official remote plugin marketplace is invalid
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16).
Data as JSON: /api/errors/ec776213979574fa.
Report an issue: GitHub.