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.
What happened
In the lakeFS Web UI, the built-in "Query with DuckDB" feature fails with:
whenever the object path contains a non-ASCII character (e.g. CJK), and also for paths containing characters that
encodeURIComponentescapes, such as=(common in Hive-style partitioningcol=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 vialakectl/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,extractFilesencodes each path segment once and registers it with duckdb-wasm as an S3-protocol URL: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
encodeURIComponentis a no-op onA-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:Observed:
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
encodeURIComponentbefore 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
.../col=*/part.parquetalso 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.extractFilespre-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
local