Skip to content

Read the allowed folder when a file source names none - #217

Merged
MikeAlhayek merged 1 commit into
mainfrom
ma/file-source-optional-folder
Sep 22, 2026
Merged

MikeAlhayek merged 1 commit into
mainfrom
ma/file-source-optional-folder

Conversation

@MikeAlhayek

Copy link
Copy Markdown
Member

The problem

A FileSystem file source that leaves its folder blank is refused with "No folder is configured.", discovers nothing, and records PartiallyCompleted / 0 ingested, 0 removed, 0 failed.

Blank is the value a host stores when the source is pointed at the allowed root itself, and it is the value an editor that presents the folder as optional produces by default. So the simplest configuration anyone can arrive at — name the source, pick a data source, leave the folder alone — was the one that silently ingested nothing.

The change

FileSystemConnectorOptions.TryResolveRoot now sends a blank rootPath to a new private TryResolveDefaultRoot, which resolves the single allowed root.

It resolves the allowed root rather than BasePath on purpose: a host sets BasePath to its content root and AllowedRoots to something like App_Data/file-sources, so "blank means BasePath" would resolve outside the boundary and fail just as silently, only with a more confusing reason.

It is a default only while there is one allowed root. With several there is no "the" folder, and picking one — the first, say — would read files nobody pointed the source at, so that case is refused with a reason that says to name one. A host that allows none gets the same refusal a named folder gets. Blank entries in AllowedRoots are not counted.

FileSystemIngestionConnector.ValidateAsync no longer refuses a blank folder ahead of the resolver, so the reason a reader sees is always the resolver's own.

The substitution is at resolution time rather than on the record, because a source created by a recipe, an import or the API never passes through an editor.

What is not affected

Containment is untouched. A .. segment is still refused outright before any path is combined, a folder outside every allowed root is still refused, and FetchAsync still checks every item id it is handed. The only value whose meaning changed is the empty one, which previously meant "refuse".

Tests

Six new tests, and all 70 under CrestApps.Core.Tests.Core.FileSources pass:

  • blank, empty and whitespace folders resolve to the single allowed root
  • blank is refused when several roots are allowed
  • blank is refused when no root is allowed
  • blank entries in AllowedRoots are ignored when deciding the default
  • a connector given no folder lists the allowed root's files and validates clean

🤖 Generated with Claude Code

A file-system file source that left its folder blank was refused with "No
folder is configured." and ingested nothing: the editor offers the folder as
optional and documents an empty value as "read the root itself", so the one
setup a reader arrives at with nothing to narrow down was the one that
silently did nothing, and the run recorded three zeroes with no visible
reason.

A blank folder now resolves to the folder the host allows the connector to
read, which is the only folder that value could have meant. It is a default
only while there is a single allowed root -- with several there is no "the"
folder, and picking one would read files nobody pointed the source at, so
that case is refused with a message that says to name one. Validation no
longer refuses a blank folder ahead of the resolver, so the reason a reader
sees is always the resolver's own.

The substitution is at resolution time rather than on the record, because a
source created by a recipe, an import or the API never passes through the
editor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@MikeAlhayek
MikeAlhayek merged commit 565a7b9 into main Sep 22, 2026
9 checks passed
@MikeAlhayek
MikeAlhayek deleted the ma/file-source-optional-folder branch September 22, 2026 14:54
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.

1 participant