paperclipai/paperclip · error · RunnerGoalActionError
session_unavailable
session_unavailable
Error message
session_unavailable: Agent task session is unavailable.
What it means
Inside the transactional block of `act`, the code selects an `agentTaskSessions` row keyed by (companyId, agentId, adapterType, taskKey=issueId) FOR UPDATE. If no session row exists it throws `session_unavailable`. A goal action requires a live agent task session to attach to; goals cannot exist sessionless.
Source
Thrown at server/src/services/runner-goals.ts:385
initialProjection.capability.reasonCode ?? "session_goals_unsupported",
initialProjection.capability.reason ?? "Session goals are unsupported.",
);
}
const accepted = await db.transaction(async (tx) => {
await tx.insert(agentTaskSessions).values({
companyId,
agentId: request.agentId,
adapterType: binding.agent!.adapterType,
taskKey: issueId,
}).onConflictDoNothing();
const [session] = await tx.select().from(agentTaskSessions).where(and(
eq(agentTaskSessions.companyId, companyId),
eq(agentTaskSessions.agentId, request.agentId),
eq(agentTaskSessions.adapterType, binding.agent!.adapterType),
eq(agentTaskSessions.taskKey, issueId),
)).limit(1).for("update");
if (!session) throw new RunnerGoalActionError("session_unavailable", "Agent task session is unavailable.");
const currentGoal = storedGoal(session);
const currentProjection: RunnerGoalProjection = {
...initialProjection,
sessionId: session.id,
capability: storedCapability(session, fallbackCapability),
goal: currentGoal,
workingNow: currentGoal?.workingNow ?? false,
revision: session.goalRevision,
observedAt: session.goalObservedAt?.toISOString() ?? null,
};
const [existingAction] = await tx.select().from(agentSessionGoalActions).where(and(
eq(agentSessionGoalActions.sessionId, session.id),
eq(agentSessionGoalActions.requestId, request.requestId),
)).limit(1);
if (existingAction) {
if (!isSameGoalActionRequest(existingAction.payloadJson, request)) {View on GitHub (pinned to 01ad858492)
Solutions
- Start an agent task session for the issue first (let the agent check out/start work), then send goal actions
- Re-check session existence via the sessions API before calling `act`
- If the session died, restart the agent run and re-establish the session before managing goals
- Verify the agentId and adapterType in your request match the session that was actually created
Example fix
// before
await runnerGoals.act(companyId, issueId, { agentId, action: 'pause' }); // session may not exist
// after
const sessions = await listAgentTaskSessions(companyId, agentId, issueId);
if (sessions.length === 0) await startAgentTask(companyId, issueId, agentId);
await runnerGoals.act(companyId, issueId, { agentId, action: 'pause' }); Defensive patterns
Strategy: validation
Validate before calling
const sessions = await listAgentTaskSessions(companyId, { agentId, taskKey: issueId });
if (sessions.length === 0) throw new Error('no agent task session for this issue'); Type guard
function hasSession(sessions) {
return Array.isArray(sessions) && sessions.length > 0;
} Try / catch
try {
await runnerGoals.act(companyId, issueId, req);
} catch (e) {
if (e instanceof RunnerGoalActionError && e.code === 'session_unavailable') {
await startAgentTask(companyId, issueId, req.agentId); // then retry
} else throw e;
} Prevention
- Ensure the agent started the task (session exists) before goal actions
- Re-verify session existence after agent restarts or crashes
- Match agentId/adapterType exactly with the created session
- Treat session teardown as invalidating pending goal actions
When it happens
Trigger: Calling a goal action when no agent task session has been started for that issue/agent; the session was terminated or cleaned up before the action; adapterType mismatch between the stored agent and session rows so the WHERE clause misses; issueId not matching session.taskKey.
Common situations: Runner retries goal actions after session teardown; agent crashed and its session was reaped; calling goals before the agent has started working on the issue (session not yet created); reassignment changed adapterType.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- OpenCode thread is not open
- opencode_run_attach_busy
- workflow eval run ${input.runId} is missing
- goal_not_found
- SETUP_TOKEN_SESSION_NOT_FOUND
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/6c1b0ef9e2734bb0.
Report an issue: GitHub.