{"record":{"id":"0edd1e7e2aebe047","repo":"janhq/jan","slug":"jan-data-folder-is-unavailable","errorCode":null,"errorMessage":"Jan data folder is unavailable","messagePattern":"Jan data folder is unavailable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"web-app/src/lib/agentWorkspace.ts","lineNumber":26,"sourceCode":"import { getServiceHub } from '@/hooks/useServiceHub'\nimport { invalidateMemory } from '@/lib/agentTools'\nimport * as skillStore from '@/lib/skillStore'\nimport type { SkillMeta } from '@/lib/skillStore'\n\nexport type { SkillMeta }\n\n/**\n * The management surface over the agent's permanent store, for the settings UI.\n * Separate from `agentTools.ts`, which is the chat-loop surface: this one edits\n * what the agent remembers, that one executes what the model calls.\n *\n * Errors are thrown rather than swallowed. Unlike a tool call, a failed edit has\n * a user waiting on it, so the page surfaces it.\n */\n\nconst dataFolder = async (): Promise<string> => {\n  const folder = await getServiceHub().app().getJanDataFolder()\n  if (!folder) throw new Error('Jan data folder is unavailable')\n  return folder\n}\n\n/** Path of the permanent store, created if absent. */\nexport async function storePath(): Promise<string> {\n  return await workspacePath(await dataFolder())\n}\n\n// Skill CRUD is shared with the code screen's per-project skills; only the root\n// differs. These bind this page to the permanent store.\nexport const listSkills = () => skillStore.listSkills(skillStore.storeScope)\nexport const readSkill = (name: string) =>\n  skillStore.readSkill(skillStore.storeScope, name)\nexport const writeSkill = (name: string, content: string) =>\n  skillStore.writeSkill(skillStore.storeScope, name, content)\nexport const deleteSkill = (name: string) =>\n  skillStore.deleteSkill(skillStore.storeScope, name)\n","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/janhq/jan/blob/7205d770c1e097c3daf35a911176410e93bc5564/web-app/src/lib/agentWorkspace.ts#L8-L44","documentation":"Thrown by the agentWorkspace helper dataFolder() when getServiceHub().app().getJanDataFolder() resolves to an empty/undefined folder path. All workspace operations (storePath, memory CRUD) depend on this base path, so the error is raised eagerly rather than writing to an invalid location.","triggerScenarios":"Any call to storePath, listMemories, readMemory, writeMemory, or deleteMemory while the app service returns a falsy Jan data folder — typically during early startup before the app service is ready, or when the data folder setting is unset/failed to resolve.","commonSituations":"Calling workspace APIs before app initialization completes; the Jan data folder configuration was cleared; the app service failed to determine the default data directory (e.g. on first launch or after a failed migration).","solutions":["Ensure the app is fully initialized before invoking workspace/memory APIs","Check that the Jan data folder is set in app settings and points to a valid directory","Restart the app so getJanDataFolder() re-resolves the default path","If the setting is corrupted, reset it to the default data directory"],"exampleFix":"// before\nconst path = await storePath()\n// after\nlet path: string\ntry {\n  path = await storePath()\n} catch {\n  path = await ensureDefaultWorkspace()\n}","handlingStrategy":"try-catch","validationCode":"const folder = await getServiceHub().app().getJanDataFolder()\nif (!folder) throw new Error('Jan data folder is unavailable — check app settings or wait for initialization')","typeGuard":"const hasDataFolder = (f: unknown): f is string =>\n  typeof f === 'string' && f.trim().length > 0","tryCatchPattern":"try {\n  await writeMemory(key, value)\n} catch (e) {\n  if (e.message === 'Jan data folder is unavailable') {\n    showToast('Workspace unavailable — the app is still initializing or the data folder is unset.')\n  }\n}","preventionTips":["Gate workspace features behind an 'app ready' signal","Validate the data folder setting at startup","Reset to a default data directory when the setting is empty","Retry after a short delay during early startup"],"tags":["configuration","filesystem","initialization"],"backgroundTag":"missing-config-value","analyzedSha":"7205d770c1e097c3daf35a911176410e93bc5564","analyzedAt":"2026-09-17T14:27:30.100Z","contentChangedAt":"2026-09-17T14:27:30.100Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}