Monitor LangChain: dead-man's-switch for scheduled agent and chain runs
Wrap
chain.invoke()
or
agent.run()
with three lines of Python, or connect the LastPing MCP server and let
the agent instrument itself. Catches missed scheduled runs, hung LLM
calls, tool timeouts, and chain exceptions. Free, no monitor cap.
Your assistant can do this for you
Monitor my LangChain agent with LastPing
On this page
LangChain agents fail silently at the boundary.
LangChain has excellent tracing (LangSmith) for what happens inside a run, but no built-in monitoring for whether the run happened at all, or whether it's currently blocked waiting forever.
LLM call never returns
A provider outage, a context-length edge case, or a rate limit
without a configured timeout means
chain.invoke()
blocks indefinitely. No exception is raised: the call is in
flight, waiting. LangSmith records nothing until the call returns.
Tool hangs or loops
A custom tool, a retriever, or a tool calling an external API can hang or retry indefinitely. The agent loop continues waiting for the tool result. No completion, no exception, no observable signal from outside the process.
Scheduled run never fires
The cron job or scheduler that invokes the chain exits early, gets OOM-killed, or is never re-scheduled after a host restart. The chain never runs, and LangSmith shows no trace for the run that didn't happen.
Setup: three lines of Python
No LangChain plugin, callback, or middleware needed. Plain HTTP calls bracket the chain invocation.
Agent self-registration via MCP
Add the LastPing MCP server at mcp.lastping.dev to your
LangChain agent's available tools or MCP server list. Prompt the
agent: "Register a dead-man's-switch for yourself: run at 3 AM daily,
45-minute grace, alert me on Telegram if missed. Get the ping snippets and
add them to your run loop." The agent calls
create_monitor → get_ping_instructions → wires
pings into its own code. Learn about the LastPing MCP server →
Create a heartbeat monitor in LastPing
In the console, create a monitor, choose Cron job, set the schedule to your chain's schedule, and copy the ping URL.
https://ping.lastping.dev/<uuid>
Wrap your chain or agent invocation
Three lines: start ping before, success ping after, fail POST in
the except block. Works identically for
LLMChain,
AgentExecutor,
CompiledGraph,
or any callable that may hang or throw.
import requests # or httpx
PING_URL = "https://ping.lastping.dev/<uuid>"
def run_chain():
requests.get(f"{PING_URL}/start", timeout=10) # arm watchdog
try:
result = chain.invoke({"input": "Run the weekly digest."})
requests.get(PING_URL, timeout=10) # signal success
return result
except Exception as exc:
requests.post(
f"{PING_URL}/fail",
data=f"{type(exc).__name__}: {exc}",
headers={"Content-Type": "text/plain"},
timeout=10,
)
raise
For async chains: use httpx.AsyncClient
The ping endpoint is a plain HTTP GET/POST: works with any HTTP
client. For async LangChain chains
(chain.ainvoke()), use
httpx.AsyncClient:
import httpx
async def run_chain_async():
async with httpx.AsyncClient() as client:
await client.get(f"{PING_URL}/start", timeout=10)
try:
result = await chain.ainvoke({"input": "..."})
await client.get(PING_URL, timeout=10)
return result
except Exception as exc:
await client.post(
f"{PING_URL}/fail",
content=f"{type(exc).__name__}: {exc}",
headers={"Content-Type": "text/plain"},
)
raise
Optional: report a step per chain stage to catch a stall mid-run
A long chain can hang inside one stage (a retriever, a tool, an LLM call that never returns) while the process stays alive. Overrun detection catches that only once the
whole run budget expires: hours, if that is what you set.
Reporting progress closes the gap: generate a run id, pass it as
?rid= on the start and completion pings, and call
the step endpoint as each chain stage finishes.
import uuid, requests
rid = uuid.uuid4().hex
requests.get(f"{PING_URL}/start", params={"rid": rid}, timeout=10)
for name, stage in stages:
stage.invoke(state)
requests.get(f"{PING_URL}/step",
params={"rid": rid, "step": name}, timeout=10)
requests.get(PING_URL, params={"rid": rid}, timeout=10)
Then set step_timeout_s on the monitor to the
longest gap you expect between steps. If nothing reports for that
long, a stalled incident opens naming the step the
run wedged in: minutes in, rather than at the end of the run
budget. This is opt-in: leave step_timeout_s unset
and the monitor behaves exactly as it did before. A step resets
the stall clock only: it never extends the run budget.
What this catches
Orthogonal to LangSmith tracing: catches the failure modes that happen outside the chain's execution boundary.
- Missed scheduled runs: scheduler crash, OOM kill, cron misconfiguration. No start ping in the expected window → incident.
- Hung LLM calls or tool calls: chain.invoke() blocks; overrun detection opens an incident after the grace window.
- Stalled runs: with step reporting enabled, a run that stops making progress opens an incident naming the chain stage it wedged in, without waiting out the whole run budget.
- Chain exceptions: the except block fires immediately, POSTing the exception type and message (up to 10,000 bytes) to the incident.
- Works with LangSmith: the two systems are orthogonal; LangSmith traces what happens inside runs, LastPing monitors whether runs happen at all and whether they complete.
- Agent self-registration: the agent can create its own monitor and wire pings via the MCP server, with no human in the loop.
Questions people ask
Front-loaded answers: the most important fact first.
-
How do I monitor a LangChain agent running on a schedule?
Wrap your
chain.invoke()orAgentExecutor.run()call with a start ping before, a success ping after, and a fail POST in the exception handler. Create a heartbeat monitor in LastPing with your schedule so a run that never starts also opens an incident. Or connect the LastPing MCP server and let the agent instrument itself. Free for individuals, no monitor cap. -
What causes LangChain agents to hang?
LangChain agents hang when they block waiting for an LLM response that never arrives (provider outage, network partition, rate limit without a
request_timeoutset), when a tool call doesn't return or blocks indefinitely (slow database, hung subprocess, infinite retry in a custom tool), or when the agent loops on a reasoning step without hitting a stop condition, such as an agent loop that exceeds max_iterations but catches theOutputParserExceptionand retries forever. In all these cases,chain.invoke()orAgentExecutor.run()never returns and no exception propagates: the process simply hangs, and the start ping fires but the success or fail ping never arrives. -
Can I use LangChain callbacks for monitoring instead?
LangChain callbacks (
BaseCallbackHandler) fire on chain start, end, and error events but run inside the same process, so they can't detect hung calls (the thread is blocked) or missed scheduled executions (the process never started). The LastPing approach is complementary: the start and success pings bracket the entire run, and the external watchdog catches silence (hung runs, missed schedules) that's invisible from inside the chain's callback system. -
Does this work with LangGraph agents?
Yes. LangGraph's
CompiledGraph.ainvoke()orinvoke()is a standard Python async coroutine or synchronous call, so wrapgraph.invoke()orgraph.ainvoke()the same way: start ping beforeinvoke(), success ping after, fail POST in the except block. LangGraph's checkpointing doesn't change the external observability gap: the heartbeat watchdog catches hung, missed and never-started runs regardless of which orchestration layer (LangChain, LangGraph, or custom) is used. -
Is LangChain monitoring free in LastPing?
Yes. LastPing is free for individuals with no monitor cap. The MCP server at
mcp.lastping.devis included. The same pattern applies to OpenAI Agents SDK and CrewAI agents: see OpenAI Agents SDK monitoring and CrewAI monitoring.
Your chain's next hung LLM call is waiting.
Three lines of Python now beats a post-mortem later. First monitor up in under two minutes, and LastPing monitors itself the same way.