diff --git a/lightmotif-py/lightmotif/io.rs b/lightmotif-py/lightmotif/io.rs
index fd32a8cb4fd12f1c4f7030007369b97e0af15203..4e256cae95bb5d5bb72b902d857b03fbc70d4904 100644
--- a/lightmotif-py/lightmotif/io.rs
+++ b/lightmotif-py/lightmotif/io.rs
@@ -9,7 +9,7 @@ use lightmotif_io::error::Error;
 use pyo3::exceptions::PyOSError;
 use pyo3::exceptions::PyValueError;
 use pyo3::prelude::*;
-use pyo3::types::PyBytes;
+use pyo3::types::PyString;
 
 use super::pyfile::PyFileRead;
 use super::CountMatrixData;
@@ -163,15 +163,14 @@ pub fn load(file: Bound<PyAny>, format: &str, protein: bool) -> PyResult<Loader>
     let py = file.py();
     let pathlike = py
         .import_bound(pyo3::intern!(py, "os"))?
-        .call_method1("fsencode", (&file,));
+        .call_method1(pyo3::intern!(py, "fsdecode"), (&file,));
     let b: Box<dyn BufRead + Send> = if let Ok(path) = pathlike {
         // NOTE(@althonos): In theory this is safe because `os.fsencode` encodes
         //                  the PathLike object into the OS prefered encoding,
         //                  which is was OsStr wants. In practice, there may be
         //                  some weird bugs if that encoding is incorrect, idk...
-        let encoded = path.downcast::<PyBytes>()?;
-        let s = unsafe { std::ffi::OsStr::from_encoded_bytes_unchecked(encoded.as_bytes()) };
-        std::fs::File::open(s)
+        let decoded = path.downcast::<PyString>()?;
+        std::fs::File::open(decoded.to_str()?)
             .map(std::io::BufReader::new)
             .map(Box::new)?
     } else {