Skip to content

DuckDB-WASM UI query returns "No files found" for object paths with non-ASCII chars (or "=", space) — client-side double URL-encoding #10479

Description

@cuiqinsain-cloud

What happened

In the lakeFS Web UI, the built-in "Query with DuckDB" feature fails with:

Error: IO Error: No files found that match the pattern "lakefs://<repo>/<ref>/<path>"

whenever the object path contains a non-ASCII character (e.g. CJK), and also for paths containing characters that encodeURIComponent escapes, such as = (common in Hive-style partitioning col=value/…) or spaces.

Objects with pure ASCII-alphanumeric paths (e.g. data/detail_all.parquet, data/annual_summary/part.parquet) query fine. The object clearly exists — it's browsable in the UI and readable via lakectl/API — but the DuckDB query reports "No files found".

Root cause: client-side double URL-encoding

The lakeFS server handles non-ASCII object keys correctly. The breakage is client-side, in how the UI hands the path to duckdb-wasm.

In webui/src/pages/repositories/repository/fileRenderers/duckdb.tsx, extractFiles encodes each path segment once and registers it with duckdb-wasm as an S3-protocol URL:

const encodedPath = matches[3].replace(/''/g, "'").split('/').map(encodeURIComponent).join('/');
// ...
await db.registerFileURL(fileName, `s3://${encodedPath}?r=${r}`, DuckDBDataProtocol.S3, true);

The path is already encodeURIComponent-escaped here. duckdb-wasm's S3 filesystem then URL-encodes the object key again when it builds the actual HTTP request to the lakeFS S3 gateway, producing a double-encoded key that the gateway cannot resolve → 404 → surfaced by DuckDB as "No files found".

Because encodeURIComponent is a no-op on A-Z a-z 0-9 - _ . ! ~ * ' ( ), pure ASCII-alphanumeric paths survive the double pass unchanged and work. Any character it does escape (CJK, =, space, etc.) gets its % re-escaped to %25… and breaks.

Minimal reproduction (server handles single-encode, breaks on double-encode)

Create an object whose key has a non-ASCII char and an =, then hit the objects endpoint with single vs. double URL-encoding:

KEY='repro/名前=1/data.csv'
lakectl fs upload -s ./data.csv "lakefs://myrepo/main/$KEY"    # object now exists (lakectl fs stat confirms)

SINGLE=$(python3 -c "import urllib.parse;print(urllib.parse.quote('repro/名前=1/data.csv'))")
DOUBLE=$(python3 -c "import urllib.parse;print(urllib.parse.quote(urllib.parse.quote('repro/名前=1/data.csv')))")

curl -s -u "$AK:$SK" -o /dev/null -w '%{http_code}\n' "$LAKEFS/api/v1/repositories/myrepo/refs/main/objects?path=$SINGLE"
# => 200

curl -s -u "$AK:$SK" -o /dev/null -w '%{http_code}\n' "$LAKEFS/api/v1/repositories/myrepo/refs/main/objects?path=$DOUBLE"
# => 404   <-- this is what the UI ends up sending

Observed:

single-encoded key: repro/%E5%90%8D%E5%89%8D%3D1/data.csv        -> HTTP 200
double-encoded key: repro/%25E5%2590%258D%25E5%2589%258D%253D1/data.csv -> HTTP 404

The server is fine; the UI's double-encoded request 404s.

Expected behavior

The DuckDB-WASM UI query should be able to read objects whose keys contain non-ASCII characters (and =, spaces, etc.). The object key should be encoded exactly once by the time it reaches the lakeFS S3 gateway.

Suggested direction

Avoid pre-encoding the path with encodeURIComponent before handing it to duckdb-wasm's S3 filesystem (which encodes the key itself), or pass the raw key and let a single encoding layer own it. Worth adding a regression test with a CJK + = key.

Notes / scope

  • A separate limitation (not this bug): glob patterns like .../col=*/part.parquet also return "No files found", because the S3-protocol registration only registers the exact files parsed from the SQL and does not expand globs against a listing. Filing this note for completeness; happy to open a separate issue if useful.
  • I traced the double-encoding to the extractFiles pre-encoding + duckdb-wasm S3 re-encoding based on the single/double-encode HTTP evidence above; I did not line-trace duckdb-wasm's S3 internals.

Environment

  • lakeFS / lakectl 1.83.0 (community)
  • Blockstore: local
  • Reproduced against the REST objects endpoint; same key resolution path the UI's S3 gateway request depends on.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions