#Approval Card (useInterrupt) Disappears from ChatbotSidebar

1 messages · Page 1 of 1 (latest)

somber tusk
#

I'm using the useInterrupt hook (version 2) to render an approval card in the ChatbotSidebar, but when I submit the card, it disappears from the chatbot.

Is there any alternative to prevent the card from disappearing after submission? 😩

Code snippets follow:

  useInterrupt({
    agentId: "conreq-multiagent",
    enabled: (event) => JSON.parse(event.value).type === 'hitl_brief_description',
    render: ({ event, resolve }) => {
      const quantity_req_batch = JSON.parse(event.value).quantity_req_batch || settings.quantity_req_batch;
      return (
              <InterruptFormPositiveImpactDescription
                inputCount={quantity_req_batch}
                onSubmit={resolve}
              />
      )
    }
  });
async def node(state: WorkflowState, config: Optional[RunnableConfig] = None):
        payload = interrupt(
            {"type": "hitl_brief_description", "quantity_req_batch": quantity_req_batch},
        )
        print(f"[Elicitation] payload: {payload}")
        # ------
        # internal processing
        # ------
 return Command(update={
        "messages": state.get("messages", []) + messages_to_add
    })
gleaming bayBOT
#

This solution has been AI generated

The card disappears because useInterrupt v2 treats the UI as transient. When you call resolve, the hook clears the pending interrupt to resume the agent, so the element unmounts. To keep something visible in the chat after submission, use a tool-call pattern or append a follow-up message from the backend.

Why it disappears

useInterrupt clears the pending event inside resolve (sets it to null), which removes the rendered element from the ChatbotSidebar. This is by design for blocking, human-in-the-loop UI.

Recommended alternatives

1) Model it as a Tool Call (recommended)

Tool calls persist in the chat. Render the same form via useRenderToolCall, and let the tool’s result lock in the submitted state.

// Frontend
useRenderToolCall({
  name: "hitl_brief_description",
  render: ({ args, submit }) => (
    <InterruptFormPositiveImpactDescription
      inputCount={args.quantity_req_batch}
      onSubmit={submit}
    />
  )
});

This keeps the card and its submitted state in history (the tool_call + tool_call_result messages).

2) Append a follow-up message after interrupt (backend)

After interrupt(...) returns, immediately add a summary/status message so the chat reflects the approval.

async def node(state, config=None):
    payload = interrupt({
        "type": "hitl_brief_description",
        "quantity_req_batch": quantity_req_batch
    })
    messages_to_add = [
        AIMessage(content=f"✅ Approved for batch: {payload.get('description')}")
    ]
    return Command(update={
        "messages": state.get("messages", []) + messages_to_add
    })

3) Manual render with renderInChat: false (ephemeral)

const element = useInterrupt({
  agentId: "conreq-multiagent",
  renderInChat: false,
  enabled: e => JSON.parse(e.value).type === 'hitl_brief_description',
  render: ({ event, resolve }) => (
#
    <InterruptFormPositiveImpactDescription
      inputCount={JSON.parse(event.value).quantity_req_batch || settings.quantity_req_batch}
      onSubmit={resolve}
    />
  )
});
// Render {element} in your sidebar, but it will still unmount after resolve.

Docs: useInterrupt (v2) behavior and human-in-the-loop flow show this lifecycle and the tool-call alternative.

Was this helpful?

If this solution worked for you, please click on the appropriate option below to help us improve:

gleaming bayBOT
#

Hi again @somber tusk, Just checking in - did that answer your question?

somber tusk
#

I'll try the third option. But I have a question: is the first option compatible with langgraph interrupts?