Apache Maka SessionStatus States and Blocked Session Handling Explained
Apache Maka defines five SessionStatus states—active, running, waiting_for_user, blocked, and aborted—with blocked sessions recording a typed blockedReason that drives both recovery logic and UI presentation.
The SessionStatus enum in Apache Maka forms the backbone of conversation lifecycle management. This open-source framework models user-driven AI sessions as state machines, where transitions between statuses trigger persistence updates, UI changes, and recovery workflows. Understanding these states—and specifically how the blocked state enables graceful error recovery—is essential for building resilient Maka applications.
The Five SessionStatus States in Apache Maka
The canonical definition lives in packages/core/src/session.ts. The SESSION_STATUSES array and accompanying TypeScript types define five distinct phases:
-
active— The session exists in storage but no Runtime Host has attached to it yet. This is the initial state after session creation. -
running— A live Runtime Host is actively executing the session. One or more conversation turns may be in flight, with the model generating responses or tools executing. -
waiting_for_user— The runtime has voluntarily paused because it requires additional input from the user. This commonly occurs when a permission request is pending or the workflow needs explicit confirmation. -
blocked— Execution has stopped due to a recoverable error. Unlikeaborted, blocked sessions can be resumed once the underlying issue is resolved. This state carries a mandatoryblockedReasonfield. -
aborted— The session has terminated permanently. This occurs when the user explicitly stops the session or an unrecoverable error prevents any further progress.
The SessionStatus type itself is defined as a union of these string literals, with runtime guards like isSessionStatus() available for type narrowing.
SessionBlockedReason: Why Sessions Become Blocked
When a session enters the blocked state, Apache Maka records a specific SessionBlockedReason to enable targeted recovery. Also defined in packages/core/src/session.ts, the SESSION_BLOCKED_REASONS array contains five possible values:
| Blocked Reason | Recovery Action Required |
|---|---|
NO_REAL_CONNECTION |
Configure a usable model connection before resuming |
auth |
Re-authenticate the user (e.g., refresh expired credentials) |
permission_required |
User must grant a permission through the request UI |
tool_failed |
A tool call failed; user intervention needed to retry or modify parameters |
unknown |
Unexpected error occurred; session can be retried generically |
The SessionBlockedReason type is intentionally granular. This allows both automated recovery workflows and precise UI messaging without parsing error strings.
How Blocked Session Handling Works in the Runtime
The transition to blocked status involves three coordinated components: projection helpers that build persistence patches, the session manager that applies updates, and storage layers that ensure durability.
Building Status Patches with buildStatusPatch
In packages/runtime/src/session-projection-helpers.ts, the buildStatusPatch function creates the partial update sent to durable storage:
function buildStatusPatch(
status: SessionStatus,
ts: number,
blockedReason?: SessionBlockedReason,
): Pick<SessionHeader, 'status' | 'blockedReason' | 'statusUpdatedAt'> {
return {
status,
blockedReason: status === 'blocked' ? (blockedReason ?? 'unknown') : undefined,
statusUpdatedAt: ts,
};
}
This helper enforces a critical invariant: blockedReason is only persisted when status equals blocked. For any other status, the field is explicitly cleared to undefined, preventing stale reason data from confusing recovery logic.
Mapping Errors to Blocked Reasons
The same file provides blockedReasonFromErrorReason(), which translates internal error strings into public SessionBlockedReason values:
// Internal runtime error → user-facing blocked reason
const reason = blockedReasonFromErrorReason('permission_required');
// Returns: 'permission_required' (typed as SessionBlockedReason)
This indirection layer insulates the persistence schema from internal error naming conventions.
SessionManager Persistence Flow
The SessionManager in packages/runtime/src/session-manager.ts orchestrates the actual status change. When the runtime encounters a recoverable failure, it calls setSessionStatus():
await setSessionStatus(sessionId, 'blocked', 'tool_failed');
Behind the scenes, this invokes updateHeader(sessionId, buildStatusPatch(...)) to atomically persist the new SessionHeader. The storage layer in packages/storage/src/session-store.ts applies the isSessionBlockedReason guard during serialization, ensuring type safety across the persistence boundary.
Rendering Blocked Sessions in the UI
The presentation layer converts raw status data into user-facing elements. In packages/ui/src/session-status-presentation.ts, the presentSessionStatus() function accepts a SessionStatus and optional blockedReason, returning display primitives:
import { presentSessionStatus } from '@maka/ui/session-status-presentation';
const { label, icon, variant, tone } = presentSessionStatus('blocked', 'permission_required', 'en');
// Returns localized label "Permission Required", warning variant, etc.
A companion file, conversation-copy.ts, provides message templates that incorporate the blocked reason into explanatory text. This separation between data (SessionStatus), reason (SessionBlockedReason), and presentation (presentSessionStatus) enables consistent UX across web, desktop, and CLI clients.
Complete Code Examples
Blocking a Session from Runtime Code
import { setSessionStatus } from '@maka/runtime/session-manager';
import { SessionStatus, SessionBlockedReason } from '@maka/core/session';
async function handleConnectionFailure(sessionId: string) {
const blockedReason: SessionBlockedReason = 'NO_REAL_CONNECTION';
await setSessionStatus(sessionId, 'blocked', blockedReason);
}
Reading Blocked Status from Storage
import { getSessionHeader } from '@maka/storage/session-store';
async function checkSessionHealth(sessionId: string): Promise<boolean> {
const header = await getSessionHeader(sessionId);
if (header.status === 'blocked') {
console.error(`Blocked: ${header.blockedReason}`);
return false;
}
return header.status === 'running';
}
React Component for Session Status Badge
import { presentSessionStatus } from '@maka/ui/session-status-presentation';
import type { SessionSummary } from '@maka/core/session';
function SessionStatusBadge({ session }: { session: SessionSummary }) {
const presentation = presentSessionStatus(
session.status,
session.blockedReason,
navigator.language
);
return (
<span className={`badge badge-${presentation.variant}`}>
{presentation.label}
</span>
);
}
Key Source Files for SessionStatus Handling
| File | Responsibility |
|---|---|
packages/core/src/session.ts |
Core type definitions: SessionStatus, SessionBlockedReason, SessionHeader, type guards |
packages/runtime/src/session-projection-helpers.ts |
buildStatusPatch(), blockedReasonFromErrorReason() for status transitions |
packages/runtime/src/session-manager.ts |
setSessionStatus(), updateHeader() for orchestrating persistence |
packages/storage/src/session-store.ts |
SQLite persistence with isSessionBlockedReason validation |
packages/ui/src/session-status-presentation.ts |
presentSessionStatus() for converting status to UI primitives |
Summary
-
Apache Maka defines five session states in
packages/core/src/session.ts:active,running,waiting_for_user,blocked, andaborted. -
The
blockedstate is recoverable and always carries ablockedReasonfrom the five possible values:NO_REAL_CONNECTION,auth,permission_required,tool_failed, orunknown. -
Status transitions use
buildStatusPatch()insession-projection-helpers.tsto enforce thatblockedReasononly persists alongsideblockedstatus. -
Error mapping through
blockedReasonFromErrorReason()decouples internal errors from the public schema. -
Full lifecycle flow: Runtime detects error → reason mapping → patch building →
SessionManagerpersistence →session-storedurability → UI presentation viapresentSessionStatus().
Frequently Asked Questions
What triggers a session to enter the blocked state?
A session becomes blocked when the runtime encounters a recoverable error that requires user action before execution can continue. Common triggers include: a tool call throwing an exception (tool_failed), the user needing to re-authenticate (auth), a missing model connection (NO_REAL_CONNECTION), or pending permission grants (permission_required). Unlike aborted, blocked sessions retain their conversation history and can be resumed.
How does Apache Maka prevent stale blockedReason values in session storage?
The buildStatusPatch() helper in session-projection-helpers.ts explicitly sets blockedReason to undefined whenever the status is not 'blocked'. This ensures that transitioning from blocked to running or active automatically clears the reason field. The storage layer validates this through the isSessionBlockedReason guard when reading headers back from SQLite.
Can I customize the UI messages shown for each blocked reason?
Yes, but through the presentation layer rather than core types. The presentSessionStatus() function in packages/ui/src/session-status-presentation.ts maps each SessionBlockedReason to a label, icon variant, and tone. To customize messaging, you can wrap or replace this function while keeping the underlying SessionBlockedReason types unchanged—ensuring compatibility with the runtime's error handling logic.
What is the difference between waiting_for_user and blocked?
waiting_for_user represents an intentional pause by the runtime, typically for permission requests that are part of normal workflow execution. The system expects the user to respond, but no error occurred. blocked represents an unexpected or error condition that stopped execution—while recoverable, it indicates something went wrong rather than proceeding as designed.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →