# Interrupts and message editing
URL: /docs/runtimes/langgraph/interrupts
Interrupt persistence and checkpoint-based message editing.
Both interrupt persistence and message editing rely on LangGraph's server-side checkpoints. Once you have one wired up, the other is mostly free.
## Interrupt persistence \[#interrupt-persistence]
LangGraph supports interrupting the execution flow to request user input or handle specific interactions. These interrupts can be persisted and restored when switching between threads:
1. Make sure your thread state type includes the `interrupts` field.
2. Return the interrupts from the `load` function along with the messages.
3. The runtime automatically restores the interrupt state when switching threads.
```ts
const runtime = useLangGraphRuntime({
stream: async (messages, { initialize, ...config }) => {
/* ... */
},
load: async (externalId) => {
const state = await getThreadState(externalId);
return {
messages: state.values.messages,
interrupts: state.tasks[0]?.interrupts,
};
},
});
```
This is particularly useful for applications that require user approval flows, multi-step forms, or other interactive elements that span multiple thread switches.
## Message editing and regeneration \[#message-editing-and-regeneration]
LangGraph uses server-side checkpoints for state management. To support message editing (branching) and regeneration, provide a `getCheckpointId` callback that resolves the appropriate checkpoint for server-side forking.
```ts
const runtime = useLangGraphRuntime({
stream: async (messages, { initialize, ...config }) => {
const { externalId } = await initialize();
if (!externalId) throw new Error("Thread not found");
return sendMessage({ threadId: externalId, messages, config });
},
create: async () => {
const { thread_id } = await createThread();
return { externalId: thread_id };
},
load: async (externalId) => {
const state = await getThreadState(externalId);
return {
messages: state.values.messages,
interrupts: state.tasks[0]?.interrupts,
};
},
getCheckpointId: async (threadId, parentMessages) => {
const client = createClient();
const history = await client.threads.getHistory(threadId);
for (const state of history) {
const stateMessages = state.values.messages;
if (!stateMessages || stateMessages.length !== parentMessages.length) {
continue;
}
const hasStableIds =
parentMessages.every((m) => typeof m.id === "string") &&
stateMessages.every((m) => typeof m.id === "string");
if (!hasStableIds) continue;
const isMatch = parentMessages.every(
(m, i) => m.id === stateMessages[i]?.id,
);
if (isMatch) {
return state.checkpoint.checkpoint_id ?? null;
}
}
return null;
},
});
```
When `getCheckpointId` is provided:
* **Edit buttons** appear on user messages, allowing users to edit and resend from that point.
* **Regenerate buttons** appear on assistant messages, allowing users to regenerate the response.
The resolved `checkpointId` is passed to your `stream` callback via `config.checkpointId`. Your `sendMessage` helper should map it to the LangGraph SDK's `checkpoint_id` parameter (see [quickstart](/docs/runtimes/langgraph/quickstart) for the helper).
Without `getCheckpointId`, edit and regenerate buttons will not appear. This is intentional; truncating client-side messages without forking from the correct server-side checkpoint would produce incorrect state.
## Next \[#next]