Filesystem caps reject slash paths, but glob() returns them #15

Closed
opened 2026-08-28 17:08:47 +09:00 by floot · 1 comment
Owner

The first error of the "self-hosted goo" session was:

await E(src).readText('packages/daemon/src/interfaces.js');
// Error: Path segment must not contain '/', '\\', or '\\0'

The working form is a segment array:
readText(['packages','daemon','src','interfaces.js']).

The trap is that glob() on the same capability returns slash-joined
strings
, which readText and stat then refuse. Round-tripping a glob
result through a read is a guaranteed error:

const [p] = await E(fs).glob('**/*.jsonl');   // 'projects/-workspace/x.jsonl'
await E(fs).stat(p);                          // throws

list() takes varargs segments while readText() takes an array, so which
form to use is a coin flip per method; guessing list('a','b','c') wrong
produces a bare ENOENT rather than a shape complaint.

Wanted: accept slash strings everywhere (splitting internally), or have
glob return segment arrays. Either way, one convention across the
filesystem interface.

The first error of the "self-hosted goo" session was: ```js await E(src).readText('packages/daemon/src/interfaces.js'); // Error: Path segment must not contain '/', '\\', or '\\0' ``` The working form is a segment array: `readText(['packages','daemon','src','interfaces.js'])`. The trap is that `glob()` on the same capability **returns slash-joined strings**, which `readText` and `stat` then refuse. Round-tripping a glob result through a read is a guaranteed error: ```js const [p] = await E(fs).glob('**/*.jsonl'); // 'projects/-workspace/x.jsonl' await E(fs).stat(p); // throws ``` `list()` takes varargs segments while `readText()` takes an array, so which form to use is a coin flip per method; guessing `list('a','b','c')` wrong produces a bare ENOENT rather than a shape complaint. **Wanted:** accept slash strings everywhere (splitting internally), or have `glob` return segment arrays. Either way, one convention across the filesystem interface.
Author
Owner

Fixed in 9d0f61b, live since generation 42. Path arguments are split into segments once, in normalizeSegments and resolveSegments, so a slash-joined string and a segment array name the same path across the whole mount surface and a glob() result feeds straight back into readText. . and .. still normalize per segment (so .. stays clamped at the confinement root) and denied names inside a joined path are still refused. Verified live: E(src).readText("packages/daemon/src/interfaces.js") - the exact call from this issue - now resolves.

Fixed in 9d0f61b, live since generation 42. Path arguments are split into segments once, in `normalizeSegments` and `resolveSegments`, so a slash-joined string and a segment array name the same path across the whole mount surface and a `glob()` result feeds straight back into `readText`. `.` and `..` still normalize per segment (so `..` stays clamped at the confinement root) and denied names inside a joined path are still refused. Verified live: `E(src).readText("packages/daemon/src/interfaces.js")` - the exact call from this issue - now resolves.
floot closed this issue 2026-09-01 14:05:21 +09:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
floot/endo#15
No description provided.