Skip to content

Strip trailing slash before DFS GetProperties (fixes OneLake 403)#174

Merged
benfleis merged 3 commits into
duckdb:mainfrom
djouallah:fix/onelake-dfs-trailing-slash
Jul 13, 2026
Merged

Strip trailing slash before DFS GetProperties (fixes OneLake 403)#174
benfleis merged 3 commits into
duckdb:mainfrom
djouallah:fix/onelake-dfs-trailing-slash

Conversation

@djouallah

Copy link
Copy Markdown
Contributor

Problem

On the DFS filesystem, CreateHandle passes parsed_url.path straight into GetFileClient(...), so a trailing / ends up on the wire in the subsequent GetProperties() call. A trailing slash is only a directory hint, not part of the resource name.

OneLake's DFS endpoint (*.dfs.fabric.microsoft.com) rejects a GetProperties on a trailing-slash path with 403 AuthenticationFailed / Forbidden, while the identical request without the slash returns 200. Because DirectoryExists, FileExists, and Glob (no-wildcard) all funnel through OpenFileCreateHandleLoadRemoteFileInfoGetProperties, any directory probe on OneLake fails.

This surfaces in practice as duckdb-delta partitioned writes failing: PhysicalCopyToFile probes the target table directory via DirectoryExists("…/Tables/dbo/<table>/"), producing:

IOException: AzureStorageFileSystem open file '…/Tables/dbo/<table>/' failed with code 'AuthenticationFailed', Reason Phrase: 'Forbidden'

Reproduces standalone, no write needed:

SELECT count(*) FROM glob('abfss://…@onelake.dfs.fabric.microsoft.com/…/<table>');   -- 200, works
SELECT count(*) FROM glob('abfss://…@onelake.dfs.fabric.microsoft.com/…/<table>/');  -- 403, fails

Fix

Strip a single trailing / from the path used to build the file client. info.path is left untouched, so the directory-marker detection in LoadRemoteFileInfo (StringUtil::EndsWith(afh.GetPath(), "/")) still works; only what is sent to the service changes. Real file keys never end in /, so reads/writes/Create are unaffected.

Note: only the DFS (abfss:///abfs://) filesystem is touched here since that's where OneLake lives. The Blob filesystem has the same pattern if parity is desired.

A trailing '/' is a directory hint, not part of the resource name, but it
was passed straight into GetFileClient(parsed_url.path). OneLake's DFS
endpoint rejects a GetProperties on a trailing-slash path with
403 AuthenticationFailed (the slash-less path returns 200), so any
DirectoryExists/FileExists/glob on a directory fails. This breaks e.g.
duckdb-delta partitioned writes, which probe the table directory.

Strip a single trailing slash from the path used to build the file client.
info.path is left untouched, so the directory-marker check in
LoadRemoteFileInfo still works.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@djouallah
djouallah marked this pull request as ready for review May 29, 2026 14:28
djouallah added a commit to djouallah/duckrun that referenced this pull request Jun 6, 2026
duckdb-azure's glob() can't list OneLake (abfss://) directories
(duckdb/duckdb-azure#174, unreleased): glob('<schema>/*') returns zero rows, so
glob-based relation discovery came back empty and standalone read-only commands
(dbt test/show/docs) failed with "schema does not exist" for every model. dbt
build masked it because the materialization creates the delta_scan views itself.

For abfss:// stores, enumerate the schema's table directories with the OneLake /
ADLS Gen2 DFS "List Paths" REST API (new remote.py), using the same bearer token
that authenticates the Delta reads/writes. Local and az:// stores keep using
DuckDB glob, which works there. Verified locally: standalone `dbt test` against
real OneLake now PASS=25 ERROR=0 (was: every test ERROR).

Also revert the forced curl azure transport in secret.py: it's not the fix and it
breaks non-Linux hosts (CURLE 43); delta_scan works on the default transport.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
djouallah added a commit to djouallah/duckrun that referenced this pull request Jun 8, 2026
duckdb-azure's glob() can't list OneLake (abfss://) directories
(duckdb/duckdb-azure#174, unreleased): glob('<schema>/*') returns zero rows, so
glob-based relation discovery came back empty and standalone read-only commands
(dbt test/show/docs) failed with "schema does not exist" for every model. dbt
build masked it because the materialization creates the delta_scan views itself.

For abfss:// stores, enumerate the schema's table directories with the OneLake /
ADLS Gen2 DFS "List Paths" REST API (new remote.py), using the same bearer token
that authenticates the Delta reads/writes. Local and az:// stores keep using
DuckDB glob, which works there. Verified locally: standalone `dbt test` against
real OneLake now PASS=25 ERROR=0 (was: every test ERROR).

Also revert the forced curl azure transport in secret.py: it's not the fix and it
breaks non-Linux hosts (CURLE 43); delta_scan works on the default transport.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@djouallah

Copy link
Copy Markdown
Contributor Author

@benfleis any chance you have a look

@benfleis benfleis closed this Jul 12, 2026
@benfleis benfleis reopened this Jul 12, 2026
@benfleis

Copy link
Copy Markdown
Member

Thanks @djouallah!

This needs to land against v1.5-variegata. I'll backport that one and link it here; this one will either get landed later (I need to do a v1.5 sync to main first), either directly from here or as a bigger forward port from v1.5 PR.

benfleis added a commit that referenced this pull request Jul 13, 2026
…ckport #174) (#177)

* Strip trailing slash before DFS GetProperties (fixes OneLake 403)

A trailing '/' is a directory hint, not part of the resource name, but it
was passed straight into GetFileClient(parsed_url.path). OneLake's DFS
endpoint rejects a GetProperties on a trailing-slash path with
403 AuthenticationFailed (the slash-less path returns 200), so any
DirectoryExists/FileExists/glob on a directory fails. This breaks e.g.
duckdb-delta partitioned writes, which probe the table directory.

Strip a single trailing slash from the path used to build the file client.
info.path is left untouched, so the directory-marker check in
LoadRemoteFileInfo still works.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Also:
* update v1.5 refs
* update winbuild for VS 18
* update CI build tags -> v1.5.4, also actionlint + CI yaml cleanups

---------

Co-authored-by: djouallah <djouallah@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@benfleis
benfleis merged commit 4bc0903 into duckdb:main Jul 13, 2026
13 checks passed
@djouallah
djouallah deleted the fix/onelake-dfs-trailing-slash branch July 13, 2026 22:57
@benfleis

Copy link
Copy Markdown
Member

@djouallah BTW, is this issue filed upstream with OneLake?

Is it defensive practice in DFS against the '/' trailing slash sentinals used in S3, and presumeably AZ Blob store?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants