Skip to content

Local-folder source with a relative path resolves against the Tauri build dir, and the error never says where it looked #5830

Description

@M3gA-Mind

Problem

A local-folder memory source with a relative path silently resolves against the app's process working directory — which is the Tauri build directory — instead of the workspace. The sync fails permanently with an error that names the relative path and never reveals where it actually looked.

From a live session, a source configured as docs (label "Openhuman Docs"):

01:10:48 INF  [memory_sources] sync_rpc: entry source_id=src_21d1428cbc0f49e49ac38d0104175f29
01:10:48 WRN  [memory_sync:dispatcher] pipeline tick failed
              pipeline_id="workspace:folder:src_21d1428c…"  error=not found: folder does not exist: docs
01:10:49 ERR  rpc.invoke_method failed: ai.tinyhumans.tinymemory.Error.Other:
              sync source src_21d1428c…: not found: folder does not exist: docs
              (actions_called=0, provider_cost_usd=0)

The app's actual working directory:

/Users/megamind/tinyhuman/openhuman/app/src-tauri

There is no docs there. But two plausible targets do exist:

/Users/megamind/tinyhuman/openhuman/docs   (36 entries)
/Users/megamind/tinyhuman/docs             (18 entries)

So the path is only "missing" because it is resolved against a build directory no user would think of.

Cause

FolderReader::list_itemsvendor/tinymemory/crates/tinymemory-sources/src/readers/folder.rs:41-57:

async fn list_items(
    &self,
    source: &MemorySourceEntry,
    _workspace: &std::path::Path,          // handed the workspace, deliberately ignored
) -> SourceResult<Vec<SourceItem>> {
    let base_path = source.path.as_deref()
        .ok_or_else(|| MemoryError::Invalid("folder source requires a path".to_string()))?;

    let base = PathBuf::from(base_path);   // verbatim — no join, no canonicalise
    if !base.exists() {
        return Err(MemoryError::NotFound(format!("folder does not exist: {base_path}")));
    }

folder is the only reader in the crate that ignores the workspace it is given. Every sibling takes it as a live parameter:

reader signature
conversation.rs workspace: &Path — and uses it: let threads_dir = workspace.join(…)
github.rs workspace: &Path
rss.rs workspace: &Path
web_page.rs workspace: &Path
mod.rs (trait) workspace: &Path
folder.rs _workspace: &Path

conversation.rs:35 is the in-crate precedent for exactly this: anchor a relative path on the workspace rather than the ambient CWD.

Why the UI makes it easy to hit

AddMemorySourceFields.tsx:155 renders a FolderField for the folder kind. There is no native directory picker wired to it — it is a free-text input, so a user can type docs and the value is stored verbatim. Nothing absolutises it on the way in, and nothing validates that it resolves.

Why the error is hard to diagnose

folder does not exist: docs reports the configured string, never the resolved absolute path. From the UI or the log there is no way to learn it looked in app/src-tauri. Diagnosing this required reading the reader source and then lsof-ing the running process for its CWD.

It also fails on every sync cycle — once a minute at the default cadence — so it is a permanent, repeating error for a source that could work.

Suggested direction

Not prescriptive about which layer, but the requirement is that a relative folder path either resolves somewhere defensible or is rejected at entry:

  1. Resolve relative paths against the workspace in FolderReader, matching conversation.rs. That is the smallest change and makes the ignored _workspace parameter meaningful.
  2. Name the resolved path in the error: folder does not exist: docs (resolved to /…/app/src-tauri/docs). This alone would have made the bug self-diagnosing.
  3. Absolutise or validate at entry — either wire a real directory picker to FolderField, or reject/normalise a relative path when the source is created, so an unresolvable source cannot be saved in the first place.

Deciding between "resolve against workspace" and "require absolute" is a product call. What should not stand is the current behaviour: accept a relative path, resolve it against an arbitrary build directory, and report the failure without saying where it looked.

Environment

  • main @ 7e52f3bf3, dev build, macOS
  • tinymemory pinned 1.13.2
  • Reproduced with source label "Openhuman Docs", path docs, on a fresh app start

Notes

Same family as #5820 and #5805 — a failure that is technically reported but carries too little information to act on. Here the sync surface is otherwise healthy: it reaches the pipeline and fails on data rather than on capability, which is an improvement over #5801.

Activity

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

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions