Skip to content

mkdir: missing-parent returns HTTP 500 (should be 404/409) + add recursive-create option #43

Description

@vajraimb

Summary

Two related rough edges around directory creation via POST /api/v1/directories:

  1. (bug) Creating a directory whose parent doesn't exist returns HTTP 500, even though the handler's mapErrorToStatus is already designed to return 404 for not-found (and 409 for already-exists). The FS plugins return a plain fmt.Errorf(...) that doesn't wrap the sentinel errors, so errors.Is(err, os.ErrNotExist) fails and it falls through to 500.

  2. (feature) No recursive-create (mkdir -p) option — a client must create every intermediate level itself. For a system explicitly aimed at LLM agents (which naturally emit deep paths), this is a sharp edge.

Repro

curl -s -o - -w '\nHTTP %{http_code}\n' -X POST \
  'http://localhost:8080/api/v1/directories?path=/memfs/a/b/c'
# {"error":"no such file or directory: /a/b"}
# HTTP 500        <-- expected 404 (or 409 for an existing dir)

Root cause (current master)

pkg/handlers/handlers.go mapErrorToStatus() correctly maps os.ErrNotExist / filesystem.ErrNotFound → 404, filesystem.ErrAlreadyExists → 409, etc. But the plugins return unwrapped errors. In pkg/plugins/localfs/localfs.go Mkdir:

// parent missing
return fmt.Errorf("parent directory does not exist: %s", filepath.Dir(path))  // ~L108
// already exists
return fmt.Errorf("directory already exists: %s", path)                       // ~L102

Neither wraps a sentinel, so errors.Is(...) in mapErrorToStatus can't match → 500. (memfs's Mkdir has the same shape; its message is "no such file or directory: ...".)

Suggested fixes

  • Wrap the sentinels so status mapping works and is consistent across plugins (localfs, memfs, …), e.g.:
    return fmt.Errorf("parent directory does not exist: %s: %w", parent, filesystem.ErrNotFound)
    return fmt.Errorf("directory already exists: %s: %w", path, filesystem.ErrAlreadyExists)
  • (feature) Add an optional recursive create, e.g. POST /directories?path=/a/b/c&recursive=true (or parents=true), mapping to os.MkdirAll for localfs and the equivalent for in-memory plugins. One call to create a deep path matters for the agent-first use case.

Happy to send a PR if that's helpful. Observed via the HTTP API on the localfs and memfs plugins.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions