PRQL/prql · error

Path ` ` is not valid UTF-8

Error message

Path `{}` is not valid UTF-8

What it means

When writing compiler output (e.g. pl_to_prql output for a format/translate step) to an output path, the CLI needs a &str path. If the constructed path is not valid UTF-8, run() returns this anyhow error with the path's display form. Non-UTF-8 paths are rejected rather than lossily converted.

Solutions

  1. Rename the file/directory so the full path is valid UTF-8
  2. Write to stdout (omit the output path argument) and redirect in the shell instead
  3. If --root is used, verify the joined root + relative path is UTF-8
  4. Check the path with `iconv` / `printf %s "$p" | iconv -f utf-8 -t utf-8` to locate offending bytes

Example fix

# before (path contains invalid bytes)
prqlc compile q.prql > "$OUT_DIR/café-latin1-name.sql"
# after (valid UTF-8 path)
prqlc compile q.prql > "$OUT_DIR/cafe-name.sql"
Defensive patterns

Strategy: validation

Validate before calling

# Verify output path is valid UTF-8 before running
python3 -c "import sys; open(sys.argv[1]).close()" "$OUT_FILE" 2>/dev/null || echo 'path not usable/UTF-8'

Try / catch

if ! out=$(prqlc compile q.prql 2>&1); then echo "$out" | grep -q 'not valid UTF-8' && echo 'rename output path'; fi

Prevention

When it happens

Trigger: Running a prqlc command whose output is redirected to a file whose full path contains non-UTF-8 bytes (e.g. invalid UTF-8 in a filename or a parent directory name), possibly combined with --root.

Common situations: Files created by scripts with bytes outside valid UTF-8 (common with Latin-1 encoded names), mounts with opaque byte names, or locales producing non-UTF-8 filenames on Unix.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of PRQL/prql@e164e249b9 (2026-09-09). Data as JSON: /api/errors/8d499fa655431ffc. Report an issue: GitHub.

Appendix: source

Thrown at prqlc/prqlc/src/cli/mod.rs:323

                let sources = read_files(input)?;
                let root = sources.root;

                for (path, source) in sources.sources {
                    let ast = prql_to_pl(&source)?;

                    // If we're writing to stdout (though could this be nicer?
                    // We're discarding many of the benefits of Clio here...)
                    if path.as_os_str() == "" {
                        let mut output: Output = Output::new(input.path())?;
                        output.write_all(&pl_to_prql(&ast)?.into_bytes())?;
                        break;
                    }

                    let path_buf = root
                        .as_ref()
                        .map_or_else(|| path.clone(), |root| root.join(&path));
                    let path_str = path_buf.to_str().ok_or_else(|| {
                        anyhow!("Path `{}` is not valid UTF-8", path_buf.display())
                    })?;
                    let mut output: Output = Output::new(path_str)?;

                    output.write_all(&pl_to_prql(&ast)?.into_bytes())?;
                }
                Ok(())
            }
            Command::ShellCompletion { shell } => {
                shell.generate(&mut Cli::command(), &mut std::io::stdout());
                Ok(())
            }
            Command::Debug(DebugCommand::Ast) => {
                prqlc::ir::pl::print_mem_sizes();
                Ok(())
            }
            Command::Debug(DebugCommand::JsonSchema { ir_type }) => {
                let schema = match ir_type {
                    IntermediateRepr::Pl => schema_for!(pl::ModuleDef),

View on GitHub (pinned to e164e249b9)