kitao/pyxel · error

Failed to open file '{path}'

Error message

Failed to open file '{path}'

What it means

`parse_tmx` failed to open the TMX file at the given path and returns this formatted error string via `Result::Err`. The underlying io::Error is discarded, so only the path is reported. It is the first validation step of loading a Tiled tilemap.

Source

Thrown at crates/pyxel-core/src/tmx_parser.rs:54

#[derive(Debug, Deserialize)]
#[serde(rename = "map")]
struct TmxMap {
    #[serde(rename = "@tilewidth")]
    tilewidth: u32,
    #[serde(rename = "@tileheight")]
    tileheight: u32,
    #[serde(rename = "tileset", default)]
    tilesets: Vec<Tileset>,
    #[serde(rename = "layer", default)]
    layers: Vec<Layer>,
}

pub fn parse_tmx(path: &str, layer_index: u32) -> Result<RcTilemap, String> {
    let err = |msg| format!("{msg} '{path}'");

    // Load and validate the TMX layer.
    let mut file = File::open(path).map_err(|_| err("Failed to open file"))?;
    let mut tmx_text = String::new();
    file.read_to_string(&mut tmx_text)
        .map_err(|_| err("Failed to read file"))?;

    let tmx: TmxMap = serde_xml_rs::from_str(&tmx_text).map_err(|_| err("Failed to parse file"))?;

    if tmx.tilewidth != TILE_SIZE || tmx.tileheight != TILE_SIZE {
        return Err(err("Invalid tile size in file"));
    }

    let tileset = tmx
        .tilesets
        .first()
        .ok_or_else(|| err("No tileset found in file"))?;
    let columns = tileset
        .columns
        .ok_or_else(|| err("No embedded tileset in file"))?;

View on GitHub (pinned to 50f9bd7778)

Solutions

  1. Verify the file exists at the exact path (check spelling and extension).
  2. Use an absolute path or resolve the path relative to the executable/resources, not the process cwd.
  3. Check file permissions for the running user.
  4. Confirm the asset is included in packaged builds (e.g. embedded resources or copied data dir).

Example fix

// before
let tilemap = Tilemap::from_tmx("assets/level.tmx", 0)?;
// after
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("assets/level.tmx");
assert!(path.exists(), "missing TMX: {}", path.display());
let tilemap = Tilemap::from_tmx(path.to_str().unwrap(), 0)?;
Defensive patterns

Strategy: validation

Validate before calling

// Check file presence/permissions before calling parse_tmx
fn tmx_readable(path: &str) -> Result<(), String> {
    let p = std::path::Path::new(path);
    if !p.is_file() {
        return Err(format!("TMX file missing: {}", path));
    }
    std::fs::File::open(p).map(|_| ()).map_err(|e| format!("cannot open {}: {}", path, e))
}

Try / catch

match Tilemap::from_tmx(path, 0) {
    Ok(tm) => use_tilemap(tm),
    Err(e) if e.starts_with("Failed to open file") => {
        eprintln!("TMX path bad ({}): check cwd and packaged assets", e);
    }
    Err(e) => eprintln!("TMX load failed: {}", e),
}

Prevention

When it happens

Trigger: Calling `parse_tmx(path, layer_index)` (or `Tilemap::from_tmx`) where `File::open(path)` fails: the file does not exist, the path is wrong, permission is denied, or the path is a directory.

Common situations: Typo in the .tmx filename; passing a path relative to the wrong working directory (e.g. binary run from a different cwd); missing data files in a packaged/deployed build; reading a file the user lacks permissions for.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of kitao/pyxel@50f9bd7778 (2026-09-03). Data as JSON: /api/errors/5e5c91075346e981. Report an issue: GitHub.