How to Troubleshoot Handoffs Not Appearing in the Next Agent Session with ai-memory

In ai-memory, handoffs fail to appear in subsequent agent sessions when workspace and project resolution, user ownership filters, handoff state, or current working directory constraints are not satisfied.

The ai-memory framework persists agent handoffs as first-class SQLite records, but visibility in the next session depends on strict validation rules enforced by the admission layer. If you are troubleshooting why handoffs aren't appearing in the next agent session, you must verify that the receiving context matches the exact workspace, project, ownership, and directory constraints recorded when the handoff was created. This guide examines the source code in akitaonrails/ai-memory to identify the specific conditions that filter handoffs from view.

Understanding Handoff Visibility Constraints

According to crates/ai-memory-core/src/handoff.rs, a NewHandoff persists as a row in the SQLite store with metadata including workspace, project, owner, state, and current working directory. The next agent session queries these records through the API routes defined in crates/ai-memory-web/src/routes/api.rs, but the handoff only surfaces when all of the following conditions align:

Workspace and Project Resolution

The receiving session must resolve the identical {workspace} and {project} identifiers stored in the handoff row. The overview endpoint at /workspaces/{ws}/projects/{proj}/overview filters strictly by these path parameters.

Ownership and Authentication Filters

The admission logic in crates/ai-memory-wiki/src/admission.rs applies ownership filters based on the caller's identity. An unnamed (anonymous) caller sees only handoffs where owner_user is None. A named caller (e.g., --user alice) sees handoffs owned by that user plus any unowned handoffs. Handoffs owned by other users remain invisible.

Handoff State Validation

Only rows with state = Open are advertised as pending handoffs. Once accepted (state = accepted), cancelled, or expired, the record disappears from active listings. The state machine is defined in crates/ai-memory-core/src/handoff.rs.

Current Working Directory Matching

When created via memory_handoff_begin, a handoff records the cwd (current working directory). For automatic handoff discovery, the next session must start in the exact same directory path; otherwise, the filter excludes the record.

Expiration and Decay

The background decay sweep in crates/ai-memory-consolidate/src/auto_improve.rs can mark handoffs as expired. Expired rows are omitted from all API responses and CLI listings.

Step-by-Step Troubleshooting Workflow

Follow this diagnostic sequence to identify why a specific handoff is not surfacing:

  1. Verify the handoff was persisted

    Execute the CLI list command to query the store directly:

    ai-memory handoff list --workspace default --project my_proj

    If the output is empty, the handoff never reached the database.

  2. Inspect the stored row directly

    Use debug builds to examine the raw record:

    let handoff = store.reader.get_handoff_by_id(handoff_id).await?;
    println!("{:#?}", handoff);

    Check fields including owner_user, state, and cwd.

  3. Validate user identity alignment

    Confirm that your current user flag matches the handoff's owner_user field. Run ai-memory with --user <name> to assume a specific identity, or omit it to view only unowned handoffs.

  4. Check workspace and project scope

    Ensure the --workspace and --project flags (or automatic resolution from the current directory) match the values stored in the handoff exactly.

  5. Confirm directory compatibility

    Verify that your shell's current working directory matches the cwd value recorded in the handoff. Automatic handoff discovery requires an exact path match.

  6. Check for expiration

    Review the decay sweep logs or run a manual sweep in a dev build to see if the handoff was marked expired due to age or inactivity.

  7. Query the API directly

    Test the REST endpoint to bypass CLI caching:

    curl http://127.0.0.1:49374/api/v1/workspaces/default/projects/my_proj/overview

    If the JSON response contains "handoff": null, one of the above filters is blocking visibility.

Creating and Accepting Handoffs in Code

When debugging programmatically, you can manually create a handoff using the core library:

use ai_memory_core::handoff::{NewHandoff, HandoffState};
use ai_memory_store::WriterHandle;

let new = NewHandoff {
    workspace_id: ws_id,
    project_id: proj_id,
    from_session_id: Some(session_id),
    from_agent: AgentKind::ClaudeCode,
    to_agent: None,
    cwd: Some(std::env::current_dir()?.into()),
    summary: "Reached the end of the analysis".into(),
    open_questions: vec!["What is the next step?".into()],
    next_steps: vec!["Run tests".into()],
    files_touched: vec!["src/main.rs".into()],
    owner_user: Some("alice".into()),
};
writer.insert_handoff(new).await?;

To accept a handoff in the receiving session:

use ai_memory_core::handoff::HandoffAcceptance;

let accept = HandoffAcceptance {
    handoff_id,
    workspace_id: ws_id,
    project_id: proj_id,
    accepting_agent: AgentKind::Codex,
    accepting_session: Some(new_session_id),
    accepting_user: Some("alice".into()),
    owner_filter: OwnerFilter::UserOwned,
    receiving_cwd: Some(std::env::current_dir()?.to_string_lossy().into()),
};
writer.accept_handoff(accept).await?;

Key Source Files Reference

Understanding these files helps trace visibility logic:

Summary

Troubleshooting missing handoffs in ai-memory requires systematic verification of persistence and filtering constraints:

  • Handoffs must persist with matching workspace_id and project_id to the next session's context.
  • User identity determines visibility through owner_user filtering in the admission layer.
  • Only handoffs with state = Open appear in listings; accepted or expired records are hidden.
  • The current working directory must match the recorded cwd for automatic handoff discovery.
  • Direct API inspection and CLI list commands reveal whether records exist but are filtered.

Frequently Asked Questions

Why does my handoff disappear after I accept it?

Once you call accept_handoff() or the receiving agent claims the handoff, the system updates the state column from Open to Accepted in crates/ai-memory-core/src/handoff.rs. The listing queries intentionally filter out non-open states to prevent duplicate processing of completed work transfers.

Can multiple users see the same handoff?

Yes, but with restrictions. Unowned handoffs (owner_user = None) are visible to all authenticated and anonymous users. Owned handoffs are visible only to the specific user and to anonymous callers, but not to other named users. This isolation is enforced in crates/ai-memory-wiki/src/admission.rs.

How does the current working directory affect handoff visibility?

When memory_handoff_begin creates a handoff, it captures std::env::current_dir() into the cwd field. The next session must execute from the identical absolute path for automatic discovery. If you start the agent from a different directory, pass the specific handoff ID manually or change to the correct directory before launching.

What causes handoffs to expire automatically?

The decay sweep process in crates/ai-memory-consolidate/src/auto_improve.rs periodically evaluates handoff age and relevance. If a handoff remains unaccepted for a configurable duration or the system determines the context is stale, it updates the state to Expired. Expired handoffs are permanently excluded from the handoff list command and overview API responses.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →