NousResearch/hermes-agent · error

That project has no folder to move into

Error message

That project has no folder to move into

What it means

Thrown by moveSessionToProject when the project node selected for a session move has no folder cwd to anchor it. The desktop store resolves the target project's root working directory via projectRootCwd() from the $projectTree; if the node is missing (id not found) or is not a folder-backed project, cwd is null and the move is refused before any gateway RPC is issued. The backend's session.workspace.move requires a real cwd because it re-anchors both the session's directory grouping and any live agent.

Source

Thrown at apps/desktop/src/store/projects.ts:534

  branch?: null | string
  cwd?: string
  git_repo_root?: null | string
}

// Re-home a stored session into another project's root folder — the fix for a
// chat created in the wrong directory. The backend replaces cwd + git identity
// (so the tree's grouping follows) and re-anchors any live agent bound to the
// row; here we mirror the move into the `$sessions` cache so both the flat list
// and the grouped tree reflect it before the next authoritative refresh.
export async function moveSessionToProject(
  sessionId: string,
  projectId: string,
  profile?: null | string
): Promise<void> {
  const cwd = projectRootCwd($projectTree.get().find(node => node.id === projectId))

  if (!cwd) {
    throw new Error(translateNow('sidebar.projects.moveNoFolder'))
  }

  const res = await gatewayRequest<WorkspaceMovePayload>('session.workspace.move', {
    cwd,
    session_key: sessionId,
    ...(profile ? { profile } : {})
  })

  const moved = res.cwd || cwd
  setSessions(prev =>
    prev.map(s =>
      sessionMatchesStoredId(s, sessionId)
        ? { ...s, cwd: moved, git_branch: res.branch ?? null, git_repo_root: res.git_repo_root ?? null }
        : s
    )
  )
  void refreshProjectTree()
}

View on GitHub (pinned to c896c09c42)

Solutions

  1. Verify the projectId exists in $projectTree.get() before calling, and refresh the tree if it is stale.
  2. Ensure the target node is a folder-backed project (has a root cwd) — virtual groupings cannot accept moved sessions.
  3. Guard the call with projectRootCwd(...) yourself and surface a 'pick a folder-backed project' message instead of letting it throw.
  4. Check the profile argument matches the tree you searched — a mismatched profile yields a node miss.

Example fix

// before
await moveSessionToProject(sessionId, projectId)

// after
const node = $projectTree.get().find(n => n.id === projectId)
if (!node || !projectRootCwd(node)) {
  toast('Pick a project that has a folder.')
} else {
  await moveSessionToProject(sessionId, projectId)
}
Defensive patterns

Strategy: validation

Validate before calling

import { $projectTree } from '.../store/projects'
import { projectRootCwd } from '.../store/projects'

function canMoveToProject(projectId: string): boolean {
  const node = $projectTree.get().find(n => n.id === projectId)
  return Boolean(projectRootCwd(node))
}

Type guard

function isFolderProject(node: ProjectNode | undefined): node is ProjectNode & { cwd: string } {
  return Boolean(node && projectRootCwd(node))
}

Try / catch

try {
  await moveSessionToProject(sessionId, projectId)
} catch (e) {
  if (e instanceof Error && e.message.includes('no folder')) refreshProjectTree()
  else throw e
}

Prevention

When it happens

Trigger: Calling moveSessionToProject(sessionId, projectId) where projectId does not exist in $projectTree.get(), or where the project node is a virtual/non-folder grouping that projectRootCwd() maps to null (e.g. a profile-level or synthetic node with no folder root).

Common situations: Stale project tree after the backend refreshed projects (node id changed or was removed), racing a project deletion with a drag-and-drop move in the sidebar, or passing a project id from a different profile.

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/d7cddea43cade829. Report an issue: GitHub.