KeygraphHQ/shannon · error · Error
Workspace not found: ${workspaceName} Expected path: ${sessi
Error message
Workspace not found: ${workspaceName}
Expected path: ${sessionPath} What it means
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.
Source
Thrown at apps/worker/src/temporal/worker.ts:182
};
}
function isValidWorkspaceName(name: string): boolean {
return /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(name);
}
interface WorkspaceResolution {
workflowId: string;
sessionId: string;
isResume: boolean;
terminatedWorkflows: string[];
}
async function terminateExistingWorkflows(client: Client, workspaceName: string): Promise<string[]> {
const sessionPath = resolveSessionJsonPath(path.join('./workspaces', workspaceName));
if (!(await fileExists(sessionPath))) {
throw new Error(`Workspace not found: ${workspaceName}\n` + `Expected path: ${sessionPath}`);
}
const session = await readJson<SessionJson>(sessionPath);
const workflowIds = [
session.session.originalWorkflowId || session.session.id,
...(session.session.resumeAttempts?.map((r) => r.workflowId) || []),
].filter((id): id is string => id != null);
const terminated: string[] = [];
for (const wfId of workflowIds) {
try {
const handle = client.workflow.getHandle(wfId);
const description = await handle.describe();
if (description.status.name === 'RUNNING') {
console.log(`Terminating running scan: ${wfId}`);View on GitHub (pinned to 1ae0a142f8)
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.
Example fix
// before: wrong workspace name on resume // ./shannon start -u <url> -r <repo> -w my-audt (typo) // -> 'Workspace not found: my-audt ...' // after: use the real name from `./shannon workspaces` // ./shannon workspaces -> my-audit // ./shannon start -u <url> -r <repo> -w my-audit
Defensive patterns
Strategy: validation
Validate before calling
// Before calling resume, confirm the workspace's session.json exists at either layout
import { pathExists } from 'fs-extra';
const ws = path.join('./workspaces', workspaceName);
const modern = path.join(ws, '.shannon', 'session.json');
const legacy = path.join(ws, 'session.json');
if (!(await pathExists(modern)) && !(await pathExists(legacy))) {
throw new Error(`Workspace '${workspaceName}' not found. Run \`./shannon workspaces\` to list valid names.`);
} Type guard
function isValidWorkspaceName(name: string): boolean {
return /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(name);
} Try / catch
try {
await resolveWorkspace(client, args);
} catch (e) {
if (e instanceof Error && /Workspace not found/.test(e.message)) {
// list real workspaces and either fix the name or start a new scan
const names = await listWorkspaces();
throw new Error(`Unknown workspace. Available: ${names.join(', ')}`);
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- CONFIG_VALIDATION_FAILED
- CONFIG_NOT_FOUND
- Cannot create deliverables directory at ${deliverablesDir}
- GIT_CHECKPOINT_FAILED
- Login instructions template not found
AI-assisted analysis of KeygraphHQ/shannon@1ae0a142f8 (2026-08-12).
Data as JSON: /api/errors/c5e5830cb4d42437.
Report an issue: GitHub.