{"record":{"id":"7c2e1cb5183bdf58","repo":"can1357/oh-my-pi","slug":"no-session-artifacts-unavailable","errorCode":null,"errorMessage":"No session - artifacts unavailable","messagePattern":"No session - artifacts unavailable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/artifact-protocol.ts","lineNumber":54,"sourceCode":"}\n\n/** Resolve an `artifact://` URL to its backing file without reading artifact bytes. */\nexport async function resolveArtifactFile(url: InternalUrl, context?: ResolveContext): Promise<ResolvedArtifactFile> {\n\tconst id = parseArtifactId(url);\n\n\t// Artifact ids are per-session counters; in multi-session hosts the same\n\t// id exists in several dirs. Pin resolution to the calling session's\n\t// artifacts dir first so `artifact://3` means *this* session's #3.\n\tconst dirs = artifactsDirsFromRegistry();\n\tconst pinnedDir = context?.localProtocolOptions?.getArtifactsDir?.() ?? null;\n\tif (pinnedDir) {\n\t\tconst pinnedIndex = dirs.indexOf(pinnedDir);\n\t\tif (pinnedIndex >= 0) dirs.splice(pinnedIndex, 1);\n\t\tdirs.unshift(pinnedDir);\n\t}\n\n\tif (dirs.length === 0) {\n\t\tthrow new Error(\"No session - artifacts unavailable\");\n\t}\n\n\tlet foundPath: string | undefined;\n\tlet anyDirExists = false;\n\tconst availableIds = new Set<string>();\n\n\tfor (const dir of dirs) {\n\t\tlet files: string[];\n\t\ttry {\n\t\t\tfiles = await fs.readdir(dir);\n\t\t\tanyDirExists = true;\n\t\t} catch (err) {\n\t\t\tif (isEnoent(err)) continue;\n\t\t\tthrow err;\n\t\t}\n\t\tconst match = files.find(f => f.startsWith(`${id}.`));\n\t\tif (match) {\n\t\t\tfoundPath = path.join(dir, match);","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/artifact-protocol.ts#L36-L72","documentation":"resolveArtifactFile (packages/coding-agent/src/internal-urls/artifact-protocol.ts:54) throws when artifactsDirsFromRegistry() yields zero session artifact directories — i.e. no session is registered in the AgentRegistry, so artifacts cannot exist anywhere. Artifacts are per-session; without an active session there is nothing to resolve against.","triggerScenarios":"Resolving any artifact:// URL outside an active session: no sessions registered in AgentRegistry.global(), calling the protocol handler from a bare script/SDK context before a session starts, or after the registry was cleared on session teardown.","commonSituations":"Running artifact:// resolution in a standalone test or worker process that never opened a session; invoking the resolver after the CLI session exited; embedding the library without initializing the agent registry.","solutions":["Start/open a session first so the registry has an artifacts directory, then resolve artifact:// URLs.","Ensure the resolving code runs inside the session host process, not a separate process with an empty registry.","If embedding, register the session with AgentRegistry so artifactsDirsFromRegistry() returns at least one dir.","Guard callers: skip artifact:// resolution when no session is active and surface a user-facing message instead."],"exampleFix":"// before\nconst res = await resolveArtifactFile(url); // throws if no session\n// after\nimport { AgentRegistry } from \"../registry/agent-registry\";\nif (AgentRegistry.global().list().length === 0) {\n  return \"Artifacts are unavailable outside an active session.\";\n}\nconst res = await resolveArtifactFile(url);","handlingStrategy":"try-catch","validationCode":"import { AgentRegistry } from \"../registry/agent-registry\";\nconst hasSession = AgentRegistry.global().list().length > 0;\nif (!hasSession) {\n  return \"artifact:// URLs require an active session.\";\n}","typeGuard":"function sessionAvailable(registry: { list(): unknown[] }): boolean {\n  return registry.list().length > 0;\n}","tryCatchPattern":"try {\n  const res = await resolveArtifactFile(url, context);\n} catch (err) {\n  if (err instanceof Error && err.message === \"No session - artifacts unavailable\") {\n    return \"Artifacts are unavailable outside an active session.\";\n  }\n  throw err;\n}","preventionTips":["Only expose artifact:// resolution to code running inside a live session host.","Check the agent registry has at least one session before enabling artifact features.","In SDK/embedding scenarios, initialize and register the session before resolving internal URLs.","Avoid resolving artifacts in teardown/afterExit hooks after sessions are unregistered."],"tags":["session-state","internal-urls","artifact-protocol","registry"],"backgroundTag":"no-active-session","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}