{"record":{"id":"c5e5830cb4d42437","repo":"KeygraphHQ/shannon","slug":"workspace-not-found-workspacename-expected-pat","errorCode":null,"errorMessage":"Workspace not found: ${workspaceName}\nExpected path: ${sessionPath}","messagePattern":"Workspace not found: (.+?)\nExpected path: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/worker/src/temporal/worker.ts","lineNumber":182,"sourceCode":"  };\n}\n\nfunction isValidWorkspaceName(name: string): boolean {\n  return /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(name);\n}\n\ninterface WorkspaceResolution {\n  workflowId: string;\n  sessionId: string;\n  isResume: boolean;\n  terminatedWorkflows: string[];\n}\n\nasync function terminateExistingWorkflows(client: Client, workspaceName: string): Promise<string[]> {\n  const sessionPath = resolveSessionJsonPath(path.join('./workspaces', workspaceName));\n\n  if (!(await fileExists(sessionPath))) {\n    throw new Error(`Workspace not found: ${workspaceName}\\n` + `Expected path: ${sessionPath}`);\n  }\n\n  const session = await readJson<SessionJson>(sessionPath);\n\n  const workflowIds = [\n    session.session.originalWorkflowId || session.session.id,\n    ...(session.session.resumeAttempts?.map((r) => r.workflowId) || []),\n  ].filter((id): id is string => id != null);\n\n  const terminated: string[] = [];\n\n  for (const wfId of workflowIds) {\n    try {\n      const handle = client.workflow.getHandle(wfId);\n      const description = await handle.describe();\n\n      if (description.status.name === 'RUNNING') {\n        console.log(`Terminating running scan: ${wfId}`);","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/KeygraphHQ/shannon/blob/1ae0a142f8525410a688f0309fd003cc5b1d92de/apps/worker/src/temporal/worker.ts#L164-L200","documentation":"Thrown by terminateExistingWorkflows (a plain Error, not PentestError) when the session.json for the requested workspace cannot be found at the resolved path. resolveSessionJsonPath prefers the .shannon/ internals dir and falls back to the legacy run-root; if neither yields a fileExists, the resume cannot proceed because there is no workflow id to terminate. This fires during the resume path before any Temporal call.","triggerScenarios":"resolveWorkspace calls terminateExistingWorkflows when resuming; it computes sessionPath = resolveSessionJsonPath('./workspaces/<workspaceName>') and checks fileExists. If the workspace name is wrong, the workspace was never created, the workspace dir was deleted (e.g. by ./shannon stop --clean), or it predates the .shannon/ restructure and the legacy fallback also misses, this throws.","commonSituations":"Typo in the -w <workspace> name. Resuming after ./shannon stop --clean wiped the workspace. The workspace lives under a different CWD than the one the resume is launched from. A pre-restructure workspace whose session.json the dual-read resolver cannot locate. Path case-sensitivity mismatch on a case-sensitive filesystem.","solutions":["Run ./shannon workspaces to list valid workspace names and confirm the spelling.","Check that <repo>/workspaces/<workspaceName>/.shannon/session.json (or the legacy workspaces/<workspaceName>/session.json) actually exists.","If the workspace was cleaned, start a new scan instead of resuming: ./shannon start -u <url> -r <repo> -w <new-name>.","Ensure the resume command runs from the same directory (and SHANNON state root) as the original scan.","For a pre-restructure workspace, run migrateLegacyWorkspaceLayout by starting the scan from the repo root so the migration upgrades it in place."],"exampleFix":"// before: wrong workspace name on resume\n//   ./shannon start -u <url> -r <repo> -w my-audt   (typo)\n//   -> 'Workspace not found: my-audt ...'\n// after: use the real name from `./shannon workspaces`\n//   ./shannon workspaces   -> my-audit\n//   ./shannon start -u <url> -r <repo> -w my-audit","handlingStrategy":"validation","validationCode":"// Before calling resume, confirm the workspace's session.json exists at either layout\nimport { pathExists } from 'fs-extra';\nconst ws = path.join('./workspaces', workspaceName);\nconst modern = path.join(ws, '.shannon', 'session.json');\nconst legacy = path.join(ws, 'session.json');\nif (!(await pathExists(modern)) && !(await pathExists(legacy))) {\n  throw new Error(`Workspace '${workspaceName}' not found. Run \\`./shannon workspaces\\` to list valid names.`);\n}","typeGuard":"function isValidWorkspaceName(name: string): boolean {\n  return /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(name);\n}","tryCatchPattern":"try {\n  await resolveWorkspace(client, args);\n} catch (e) {\n  if (e instanceof Error && /Workspace not found/.test(e.message)) {\n    // list real workspaces and either fix the name or start a new scan\n    const names = await listWorkspaces();\n    throw new Error(`Unknown workspace. Available: ${names.join(', ')}`);\n  }\n  throw e;\n}","preventionTips":["Always pass a workspace name with -w and keep it consistent across resume runs.","Avoid ./shannon stop --clean before a resume; it removes the workspace entirely.","Launch resume from the same repo root / state dir as the original scan.","For pre-restructure workspaces, run from the repo root so migrateLegacyWorkspaceLayout upgrades them first."],"tags":["workspace","resume","filesystem","validation"],"backgroundTag":null,"analyzedSha":"1ae0a142f8525410a688f0309fd003cc5b1d92de","analyzedAt":"2026-08-12T17:40:03.583Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}