{"record":{"id":"5930d92aa30796e4","repo":"sinelaw/fresh","slug":"no-such-folder-parentid","errorCode":null,"errorMessage":"no such folder: ${parentId}","messagePattern":"no such folder: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/fresh-editor/plugins/orchestrator.ts","lineNumber":10645,"sourceCode":"    for (const f of childFoldersOf(parent)) {\n      out.push({ folderId: f.id, name: f.name, parent: f.parent ?? null, depth });\n      walk(f.id, depth + 1);\n    }\n  };\n  walk(null, 0);\n  return out;\n}\n\nfunction apiCreateFolder(name: string, parent?: string | null): string {\n  const clean = trimmed(name);\n  // The dialog falls back to \"New Folder\" on an empty submit because a human\n  // who typed nothing meant \"just make me one\". A caller passing an empty\n  // string meant something else — most likely a variable that didn't hold\n  // what they thought — so it is an error rather than a silent default.\n  if (!clean) throw new Error(\"folder name must not be empty\");\n  const parentId = parent ?? null;\n  if (parentId !== null && !folderById(parentId)) {\n    throw new Error(`no such folder: ${parentId}`);\n  }\n  const id = createFolder(clean, parentId);\n  refreshDockTree();\n  return id;\n}\n\nfunction apiRenameFolder(folderId: string, name: string): boolean {\n  if (!folderById(folderId)) return false;\n  const clean = trimmed(name);\n  if (!clean) throw new Error(\"folder name must not be empty\");\n  renameFolder(folderId, clean);\n  refreshOpenDialog();\n  return true;\n}\n\nfunction apiDeleteFolder(folderId: string): boolean {\n  if (!folderById(folderId)) return false;\n  // `deleteFolder` reparents the subtree one level up, so nothing inside is","sourceCodeStart":10627,"sourceCodeEnd":10663,"githubUrl":"https://github.com/sinelaw/fresh/blob/67894ca5463dbd7a89bb31add4627c27d6b79d83/crates/fresh-editor/plugins/orchestrator.ts#L10627-L10663","documentation":"apiCreateFolder validates the optional parent folder id: if a parent is supplied (non-null) but no folder with that id exists, it throws. This prevents creating a folder under a dangling parent reference. Unlike the name check, the parent is genuinely optional — pass null (or omit) to create a top-level folder.","triggerScenarios":"Calling apiCreateFolder(name, parentId) where parentId was deleted, never existed, or is stale from a previous run; passing the empty string or a placeholder value instead of null for 'no parent'.","commonSituations":"Scripts caching folder ids across sessions after the parent was removed; another client concurrently deleting the parent; passing \"\" as parent when null was meant.","solutions":["Confirm the parent id exists via the folder list before creating under it","Pass null (or omit the argument) to create a top-level folder","Re-sync folder ids after any delete/rename from another client or session"],"exampleFix":"// before\napi.createFolder(name, staleParentId);\n// after\nconst parentId = staleParentId && api.listFolders().some(f => f.id === staleParentId)\n  ? staleParentId\n  : null;\napi.createFolder(name, parentId);","handlingStrategy":"validation","validationCode":"const parents = api.listFolders();\nif (parentId != null && !parents.some(f => f.id === parentId)) {\n  parentId = null; // fall back to top-level\n}","typeGuard":"function isKnownParent(id, folders) {\n  return id == null || folders.some(f => f.id === id);\n}","tryCatchPattern":"try {\n  api.createFolder(name, parentId);\n} catch (e) {\n  if (String(e).startsWith(\"no such folder\")) {\n    api.createFolder(name, null); // create at top level instead\n  } else throw e;\n}","preventionTips":["Validate parent ids against a freshly fetched folder list","Use null for top-level, never \"\" or undefined-as-string","Re-sync ids if another client/session may have deleted the parent","Keep a name→id lookup built from the current list instead of persisted ids"],"tags":["typescript","parent-folder","not-found"],"backgroundTag":"entity-not-found","analyzedSha":"67894ca5463dbd7a89bb31add4627c27d6b79d83","analyzedAt":"2026-09-13T15:04:03.701Z","contentChangedAt":"2026-09-13T15:04:03.701Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}