Handle instance termination
Someone on your team can stop a running agent from the Agent instance page in the web app, or through the API — see Instance for what termination means on the platform. This page covers how your agent finds out and how to handle it, with examples in TypeScript and Python.
How your agent finds out
Section titled “How your agent finds out”Detection is built into both SDKs; there is nothing to configure. While a run is active, the SDK learns about termination two ways:
- Span responses. Every span create and finish response carries a control signal once the instance is terminated, so an agent that is actively emitting spans finds out on its next span.
- Polling. An idle agent that isn’t emitting spans polls the instance every 30 seconds, so termination is detected within about half a minute even when the agent is quiet.
Termination is cooperative. Prefactor records the termination and signals the agent, but it does not kill your process, and it keeps accepting spans until the agent stops. Responding is up to your code — or to your integration’s middleware.
To watch for termination from outside the agent — in a dashboard or a supervisor process — subscribe to the instance over the WebSocket API. A terminated run arrives as an agent_instances/updated notification with status terminated and the operator’s reason.
Handle termination in TypeScript
Section titled “Handle termination in TypeScript”The core runtime detects termination for you and exposes it through the termination monitor: a standard AbortSignal you can check in your own loop or pass to long-running work, plus callbacks that fire the moment termination is detected. Nothing throws on its own — checking the signal is up to your code.
import { createCore } from '@prefactor/core';
const core = createCore({ transportType: 'http', httpConfig: { apiUrl: process.env.PREFACTOR_API_URL!, apiToken: process.env.PREFACTOR_API_TOKEN!, agentIdentifier: 'my-agent-v1', },});
const monitor = core.terminationMonitor;
monitor.onTerminated((reason) => { console.log('Run terminated by Prefactor:', reason);});
core.agentManager.startInstance();
try { while (!monitor.signal.aborted) { // ... your agent's next step ... }} finally { core.agentManager.finishInstance(); monitor.reset();}Finishing an already-terminated instance is treated as success, so the finally block is safe however the run ended. reset() readies the monitor for the next run and replaces the signal — read monitor.signal fresh each run rather than capturing it once.
LangChain
Section titled “LangChain”The LangChain integration (@prefactor/langchain) throws for you. Initialise through init from @prefactor/core with the LangChain provider, and the middleware raises an error named PrefactorTerminatedError at the next agent, model, or tool hook once termination is detected:
import { init } from '@prefactor/core';import { PrefactorLangChain } from '@prefactor/langchain';
const prefactor = init({ provider: new PrefactorLangChain(), httpConfig: { apiUrl: process.env.PREFACTOR_API_URL!, apiToken: process.env.PREFACTOR_API_TOKEN!, agentIdentifier: 'my-agent-v1', },});
try { await agent.invoke({ messages: [{ role: 'user', content: query }] });} catch (err) { if (err instanceof Error && err.name === 'PrefactorTerminatedError') { // The run was terminated. Clean up, then continue to the next run. } else { throw err; }} finally { prefactor.finishCurrentRun();}PrefactorTerminatedError is not an exported class — catch it by checking error.name, as above; the operator’s reason is in error.message. Call finishCurrentRun() after every run, terminated or not: it finishes the instance if it’s still open and resets the monitor for the next one.
Initialising with init from @prefactor/langchain instead — the form that returns middleware directly — detects termination internally but never throws it, and gives you no client handle to check the signal yourself.
LiveKit
Section titled “LiveKit”The LiveKit integration (@prefactor/livekit) shuts the session down without draining and finishes the run’s open spans as failed with the termination error.
AI SDK and Claude
Section titled “AI SDK and Claude”The AI SDK (@prefactor/ai) and Claude (@prefactor/claude) adapters don’t raise on termination yet. Check monitor.signal yourself in long-running code paths.
Handle termination in Python
Section titled “Handle termination in Python”The core client detects termination internally, but its monitor isn’t part of the public API — the supported reaction point is an integration’s middleware.
LangChain
Section titled “LangChain”The LangChain middleware (prefactor-langchain) raises PrefactorTerminatedError before the next agent, model, or tool hook once termination is detected. The exception is exported from prefactor_core and carries the termination reason:
import os
from langchain.agents import create_agentfrom prefactor_core import PrefactorTerminatedErrorfrom prefactor_langchain import PrefactorMiddleware
middleware = PrefactorMiddleware.from_config( api_url="https://app.prefactorai.com", api_token=os.environ["PREFACTOR_API_TOKEN"], agent_id="01exampleagentid00000000000000000", agent_name="My Agent",)
agent = create_agent(model, tools=[...], middleware=[middleware])
try: result = await agent.ainvoke({"messages": [{"role": "user", "content": query}]})except PrefactorTerminatedError as e: logger.info("Run terminated by Prefactor: %s", e.reason)finally: await middleware.close()A service that runs its agent in a loop catches the error, waits, and starts the next run as a fresh instance:
while True: try: await run_once() except PrefactorTerminatedError: logger.info("Run terminated — next run in %.0fs.", restart_delay) await asyncio.sleep(restart_delay)LiveKit
Section titled “LiveKit”The LiveKit integration (prefactor-livekit) doesn’t respond to termination yet; the run keeps recording until your process stops.
Terminate a run from the API
Section titled “Terminate a run from the API”Terminating is an operator action, not an SDK call — neither SDK exposes it. Use the HTTP API directly with an account-scoped token:
curl -X POST https://app.prefactorai.com/api/v1/agent_instance/<instance-id>/terminate \ -H "Authorization: Bearer $PREFACTOR_ACCOUNT_TOKEN" \ -H "Content-Type: application/json" \ -d '{"reason": "Runaway retry loop"}'The reason is required and is stored on the instance. Only an active run can be terminated; the request fails with a conflict otherwise. The deployment-scoped token your agent runs with cannot terminate its own instance — use an account-scoped token from the Account › API tokens tab. The full request and response shape is under POST /agent_instance/{agent_instance_id}/terminate in the API reference.
Termination vs shutdown
Section titled “Termination vs shutdown”These two are easy to conflate. Termination is the platform stopping your run; shutdown() is your process exiting cleanly — it flushes queued telemetry and closes the transport. Call shutdown() (TypeScript) or middleware.close() (Python) when your app exits, regardless of how the run ended. PrefactorShutdownError in the TypeScript SDK means telemetry couldn’t be flushed cleanly during shutdown; it is not a terminated-run signal.
Limits
Section titled “Limits”- Termination is cooperative. If your agent never observes the signal — because it does blocking work without yielding, or its integration doesn’t check — it keeps running and Prefactor keeps recording its spans.
- An idle agent can take up to about 30 seconds to notice; an agent emitting spans notices on the next span.
Verify
Section titled “Verify”Terminate a run from the Agent instance page while your agent is active. Your agent should stop at its next step — or within about 30 seconds if it’s idle — and the instance shows as Terminated with your reason.
Related
Section titled “Related”- Instance — lifecycle states and what termination records.
- Agent instance page — the Terminate control in the web app.
- Stream agent instance updates — watch a run’s state change live over the WebSocket API.
- Configuration and environment variables — transport and capture settings for both SDKs.
- TerminationMonitor — generated TypeScript API reference.
- prefactor_core.exceptions — generated Python API reference including
PrefactorTerminatedError.