# How to Implement State Operations (SetState, ReplaceState, DeletePath) in Jido

> Learn to implement Jido state operations SetState ReplaceState and DeletePath. Discover how Jido separates state mutation logic from side-effect directives for cleaner code.

- Repository: [agentjido/jido](https://github.com/agentjido/jido)
- Tags: how-to-guide
- Published: 2026-03-09

---

**Jido implements state operations as pure data structures that actions return alongside results, separating state mutation logic from side-effect directives.**

Jido, an Elixir agent framework, treats state mutations as first-class operations through the `Jido.Agent.StateOp` and `Jido.Agent.StateOps` modules. When you implement state operations like **SetState**, **ReplaceState**, and **DeletePath**, you work with pure structs that strategies apply consistently to agent state. This design keeps your action's `cmd/2` function pure while enabling complex state transformations.

## Understanding Jido State Operations Architecture

Jido separates **state operations** from **directives** to maintain clean boundaries between mutation logic and runtime effects.

### StateOp Structs Definition

The `Jido.Agent.StateOp` module in [`lib/jido/agent/state_op.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/state_op.ex) defines five primary structs for state manipulation:

- `%StateOp.SetState{}` – Deep-merges attributes into current state
- `%StateOp.ReplaceState{}` – Replaces the entire state map
- `%StateOp.DeleteKeys{}` – Removes top-level keys
- `%StateOp.SetPath{}` – Inserts values at nested paths
- `%StateOp.DeletePath{}` – Removes values from nested paths

These structs wrap raw data, making state intentions explicit and testable.

### StateOps Application Logic

The `Jido.Agent.StateOps` module in [`lib/jido/agent/state_ops.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/state_ops.ex) contains the execution engine. The `apply_state_ops/2` function iterates over operation lists, pattern-matching each struct and applying the corresponding transformation to the agent's state map.

## Available State Operations

Jido provides five distinct operations for granular state control.

### SetState – Deep Merge Attributes

**SetState** performs a deep merge of attributes into the existing state using `State.merge/2`. Use this when updating multiple fields without overwriting unrelated data.

```elixir
alias Jido.Agent.StateOp

# Returns operation that merges %{counter: 10} into current state

%StateOp.SetState{attrs: %{counter: 10, metadata: %{version: "2.0"}}}

```

### ReplaceState – Full State Replacement

**ReplaceState** substitutes the entire state map with a new one via `Map.replace/3` semantics. Use this for resets or when replacing state entirely.

```elixir

# Completely replaces agent state with new map

%StateOp.ReplaceState{state: %{status: :initialized, config: %{}}}

```

### DeleteKeys – Remove Top-Level Keys

**DeleteKeys** drops specified keys from the state root using `Map.drop/2`. This operation only affects top-level keys.

```elixir

# Removes :tmp and :debug keys from state

%StateOp.DeleteKeys{keys: [:tmp, :debug]}

```

### SetPath – Nested Value Insertion

**SetPath** walks nested maps and inserts values at specific paths using the private `deep_put_in/3` helper. This enables surgical updates to deeply nested configuration or session data.

```elixir

# Sets config.timeout to 5000 without touching other config keys

%StateOp.SetPath{path: [:config, :timeout], value: 5_000}

```

### DeletePath – Nested Key Removal

**DeletePath** removes entries at nested paths using `pop_in/2`. This targets specific nested fields without affecting parent map structure.

```elixir

# Removes session.token from nested structure

%StateOp.DeletePath{path: [:session, :token]}

```

## Implementing State Operations in Actions

Actions in Jido return a three-element tuple: `{:ok, result, [state_operations]}`. The result map merges into state automatically, while the operation list passes to `StateOps.apply_state_ops/2`.

```elixir
defmodule MyApp.Actions.IncrementCounter do
  use Jido.Action
  
  alias Jido.Agent.StateOp
  
  def run(_agent, params, _context) do
    new_count = params.current + 1
    
    {:ok, 
      %{value: new_count}, 
      [
        %StateOp.SetState{attrs: %{counter: new_count}},
        %StateOp.SetPath{path: [:metadata, :last_updated], value: DateTime.utc_now()}
      ]
    }
  end
end

```

Any struct not matching a `StateOp` type collects as an external directive for the runtime to process, maintaining separation between state changes and side effects.

## Applying State Operations Manually

Strategies like the finite-state-machine implementation in [`lib/jido/agent/strategy/fsm.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/strategy/fsm.ex) call `StateOps.apply_state_ops/2` at lines 332-341 to process operations consistently.

```elixir
alias Jido.Agent.StateOps
alias Jido.Agent.StateOp

agent = %Jido.Agent{state: %{counter: 0, config: %{timeout: 1_000}, old_key: "data"}}

operations = [
  %StateOp.SetState{attrs: %{counter: 5}},
  %StateOp.SetPath{path: [:config, :timeout], value: 2_000},
  %StateOp.DeleteKeys{keys: [:old_key]},
  %StateOp.DeletePath{path: [:config, :deprecated_flag]}
]

{updated_agent, directives} = StateOps.apply_state_ops(agent, operations)

# updated_agent.state contains: %{counter: 5, config: %{timeout: 2_000}}

```

The function returns a tuple containing the updated agent and any non-state-op directives that the caller must handle.

## Summary

- **StateOp structs** ([`lib/jido/agent/state_op.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/state_op.ex)) define pure data representations of state mutations including **SetState**, **ReplaceState**, **DeleteKeys**, **SetPath**, and **DeletePath**.
- **StateOps functions** ([`lib/jido/agent/state_ops.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/state_ops.ex)) implement the application logic through `apply_state_ops/2`, which pattern-matches operations and applies them using `State.merge/2`, `Map.drop/2`, and `pop_in/2`.
- Actions return state operations in the tuple `{:ok, result, [operations]}` to maintain pure functions while enabling complex state updates.
- The design separates state mutations from directives, ensuring strategies in files like [`lib/jido/agent/strategy/fsm.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/strategy/fsm.ex) handle state consistently across the Jido framework.

## Frequently Asked Questions

### What is the difference between StateOp and StateOps in Jido?

**StateOp** (singular) is a module containing struct definitions like `%SetState{}` and `%DeletePath{}`, located in [`lib/jido/agent/state_op.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/state_op.ex). **StateOps** (plural) is the module containing the execution functions `apply_result/2` and `apply_state_ops/2` located in [`lib/jido/agent/state_ops.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/state_ops.ex). StateOp defines *what* to do; StateOps defines *how* to do it.

### How do I update nested state values in Jido?

Use the **SetPath** operation with a path list. For example, `%StateOp.SetPath{path: [:config, :timeout], value: 5000}` updates only the nested `:timeout` key without overwriting the entire `:config` map. Jido implements this via the private `deep_put_in/3` function in [`state_ops.ex`](https://github.com/agentjido/jido/blob/main/state_ops.ex).

### Can I combine multiple state operations in a single action return?

Yes. Actions return a list of state operations that `apply_state_ops/2` processes sequentially. You can mix **SetState**, **SetPath**, **DeleteKeys**, and **DeletePath** in the same list. The function applies them in order and returns any unrecognized structs as directives for runtime handling.

### Where are state operations processed in the Jido source code?

State operations are processed in [`lib/jido/agent/state_ops.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/state_ops.ex) within the `apply_state_ops/2` function. Strategies like the FSM strategy in [`lib/jido/agent/strategy/fsm.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/strategy/fsm.ex) (lines 332-341) invoke this function to ensure consistent state mutation across different agent behaviors.