fix(terminal): verify container exists before creating terminal session
The instance status may say 'running' but the actual Docker container may have been removed (e.g. docker prune, host restart). The old code created a terminal session which immediately died because docker exec failed with 'No such container'. - Add get_container_status check in WebSocket handler before session creation - Return 4004 with clear message if container is missing - This prevents spawning zombie terminal sessions
This commit is contained in:
@@ -124,6 +124,21 @@ async def _handle_terminal_websocket(
|
||||
|
||||
logger.debug("Terminal auth passed for instance %s, user %s", instance_id, user_id)
|
||||
|
||||
# Verify the container actually exists (may have been removed/recreated)
|
||||
from src.services.docker import get_container_status
|
||||
|
||||
container_status = get_container_status(instance.container_id)
|
||||
if container_status["status"] == "not_found":
|
||||
logger.error(
|
||||
"Container %s for instance %s not found (may have been removed)",
|
||||
instance.container_id,
|
||||
instance_id,
|
||||
)
|
||||
await websocket.close(
|
||||
code=4004, reason="Container not found — restart the tool instance"
|
||||
)
|
||||
return
|
||||
|
||||
# Fetch tool type to get startup_command
|
||||
tool_type = await db_session.get(ToolType, instance.tool_type_id)
|
||||
startup_command = tool_type.startup_command if tool_type else None
|
||||
|
||||
@@ -140,7 +140,9 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
|
||||
|
||||
if (event.data instanceof Blob) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`[Terminal ${sessionId ?? "default"}] received ${(event.data as Blob).size} bytes`);
|
||||
console.log(
|
||||
`[Terminal ${sessionId ?? "default"}] received ${(event.data as Blob).size} bytes`,
|
||||
);
|
||||
event.data.arrayBuffer().then((buffer) => {
|
||||
const data = new Uint8Array(buffer);
|
||||
termRef.current?.write(data);
|
||||
@@ -311,7 +313,9 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
|
||||
// Open xterm first (must happen before fit)
|
||||
term.open(container);
|
||||
term.focus();
|
||||
console.log(`[Terminal ${sessionId ?? "default"}] xterm opened and focused`);
|
||||
console.log(
|
||||
`[Terminal ${sessionId ?? "default"}] xterm opened and focused`,
|
||||
);
|
||||
const ws = connectWebSocket();
|
||||
|
||||
// Initial fit after layout settles (terminal must be opened first)
|
||||
@@ -337,7 +341,10 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
|
||||
// Handle terminal input
|
||||
term.onData((data) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`[Terminal ${sessionId ?? "default"}] sending:`, JSON.stringify(data));
|
||||
console.log(
|
||||
`[Terminal ${sessionId ?? "default"}] sending:`,
|
||||
JSON.stringify(data),
|
||||
);
|
||||
const currentWs = wsRef.current;
|
||||
if (currentWs?.readyState !== WebSocket.OPEN) return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user