Launcher modes, Docker, desktop and library
F26 · 34 mapped behaviors in the Self-Healing Codebase Project. Follow the prerequisites, actions and expected results; expand the original CLI recipe to replay it.
778 behavior IDs · 28 families · 844 named recipes & support steps
16 October 2026 · PR #18334 at 9a3c3a69
Canvas 1.27.1 · Agent Server 1.55.0 · automation 1.20.0
This is the map at the revision above, including its open bug notes. It does not incorporate later changes on main or claim a fresh verification run. Setup & reading guide · Download the scenario inventory
All scenarios & marker key · Open a script to read Given / Do / Expect. Expand the exact recipe for replayable commands.
No scenarios match. Try a different word or reset.
F26 Launcher modes, Docker, desktop and library
34 behaviors · 38 scripts & contracts · Maintained source
Entry points & prerequisites
What this family covers
Agent Canvas ships one UI in several runtime shapes. The npm launcher (npx @openhands/agent-canvas, here node bin/agent-canvas.mjs) runs the full stack, or only the frontend or only the backends, prints its version and stack info, refuses conflicting flags, and decides whether the session key is injected into the page (loopback) or the user must paste it (--public, or a non-loopback --host). The same stack also ships as a Docker image, an Electron desktop app and an embeddable React library. Launchers tell the agent which services exist through /server_info.runtime_services and a <RUNTIME_SERVICES> block in every new conversation's system prompt.
How a user gets here
- A terminal:
npx @openhands/agent-canvas [flags]; from this checkout,node bin/agent-canvas.mjs [flags](whatcontrol-openhands launchstarts with--port, and--publicfor public mode). - The browser at the ingress URL the launcher prints (
http://localhost:8000/by default). - Backend selector (
backend-selector) → Add Backend (add-backend-menu-item) → Agent-server → Remote, and Manage Backends to remove it (see F25 for the backend registry itself). - Public mode and the API-key screen are mapped in F01 (
F01.api-key-entry,F01.onboarding-backend-step). npm run build:libfor the embeddable library; a host app imports@openhands/agent-canvas(seedocs/DEVELOPMENT.md, "Cloud organization recovery in embedded hosts").docker run ... ghcr.io/openhands/agent-canvasornpm run build:docker, thenhttp://localhost:8000/canvas.helm install agent-canvas ./helm/agent-canvason a Kubernetes cluster (helm/agent-canvas/README.md).npm run desktop(development) or a packaged build fromnpm run build:desktop; the window opens itself.- ACP agents (Claude Code, Codex, Gemini CLI) are chosen in onboarding (F01) and Settings → Agents (F13); this family does not repeat them.
Given
Preconditions:
- Baseline state (launched, doctored,
onboard --skipdone) on your own run:control-openhands launch --new --build never,export OH_VERIFY_RUN=<run from the launch JSON>,control-openhands doctor,control-openhands onboard --skip. Commands run from the checkout root. F26.desktop-*need the Electron binary (node_modules/electron/dist/electron;npx --no-install electron --versiondownloads it on first use) andXvfbplus ImageMagickimportfor headless capture. The app binds the fixed ports 8000, 18000, 18001 and 3001: check them like the blocks below and keep the run short.F26.runtime-servicesandF26.runtime-services-agent-useneed an active LLM profile (control-openhands llm preset deepseek); the second also needs the automation service running and costs two short runs (the conversation and the dispatched automation run).- The partial-stack and LAN bullets start extra launchers by hand (harness gap:
launchhas no--frontend-only,--backend-onlyor--host). Each one needs a free block of ports: these recipes use 18960–18963 and 18970–18973, plus 19961, the editor port a launcher that serves VS Code hands its agent-server (see Gotchas); check them first withfor p in 18960 18961 18962 18963 18970 18971 18972 18973 19961; do (echo > /dev/tcp/127.0.0.1/$p) 2>/dev/null && echo "$p busy"; done(no output means free). Each launcher gets a privateHOMEand state under$OH_VERIFY_RUN/private/, so it never touches~/.openhandsor your run.
VersionF26.cli-versionMapped behavior
Scenario F26--version · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoRun
node bin/agent-canvas.mjs --version; echo "exit=$?"andnode bin/agent-canvas.mjs -v; echo "exit=$?". Each prints one line equal tojq -r .version package.json(the checkout's package version, which changes with every release: compare, do not expect a number; e.g.[ "$(node bin/agent-canvas.mjs --version)" = "$(jq -r .version package.json)" ] && echo same) andexit=0.
Expect
--version and -v print the package version and exit 0.
Exact CLI recipe & bug checks
Run node bin/agent-canvas.mjs --version; echo "exit=$?" and node bin/agent-canvas.mjs -v; echo "exit=$?". Each prints one line equal to jq -r .version package.json (the checkout's package version, which changes with every release: compare, do not expect a number; e.g. [ "$(node bin/agent-canvas.mjs --version)" = "$(jq -r .version package.json)" ] && echo same) and exit=0.
InfoF26.cli-infoMapped behavior
Scenario F26--info · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoRun
node bin/agent-canvas.mjs --info. It prints@openhands/agent-canvas <version>(the--versionvalue),Default stack versions:withagent-server: <pin>andautomation: <pin>,Compatibility:agent-server: >= <minimum>,Default ports:ingress: 8000,agent-server: 18000,automation: 18001, and the override variablesOH_AGENT_SERVER_VERSION, OH_AGENT_SERVER_GIT_REF, OH_AGENT_SERVER_LOCAL_PATH/OH_AUTOMATION_VERSION, OH_AUTOMATION_GIT_REF. The three versions are read fromconfig/defaults.json(versions.agentServer,versions.automation,compatibility.minimumAgentServer) and move with every pin bump, so compare them with the file instead of expecting numbers:test "$(node bin/agent-canvas.mjs --info | awk '/^(Default stack versions|Compatibility):/{s=1;next} /^$/{s=0} s{print $NF}' | paste -sd' ' -)" = "$(jq -r '[.versions.agentServer,.versions.automation,.compatibility.minimumAgentServer]|join(" ")' config/defaults.json)" && echo match || echo mismatchprintsmatch, andprintf '%s\n' "$(jq -r .compatibility.minimumAgentServer config/defaults.json)" "$(jq -r .versions.agentServer config/defaults.json)" | sort -VC && echo pin-meets-minimum || echo below-minimumprintspin-meets-minimum(the default Agent Server passes the UI's own version gate).
Expect
--info prints the package version, the default agent-server and automation pins, the minimum agent-server version, the default ports and the override env vars.
Exact CLI recipe & bug checks
Run node bin/agent-canvas.mjs --info. It prints @openhands/agent-canvas <version> (the --version value), Default stack versions: with agent-server: <pin> and automation: <pin>, Compatibility: agent-server: >= <minimum>, Default ports: ingress: 8000, agent-server: 18000, automation: 18001, and the override variables OH_AGENT_SERVER_VERSION, OH_AGENT_SERVER_GIT_REF, OH_AGENT_SERVER_LOCAL_PATH / OH_AUTOMATION_VERSION, OH_AUTOMATION_GIT_REF. The three versions are read from config/defaults.json (versions.agentServer, versions.automation, compatibility.minimumAgentServer) and move with every pin bump, so compare them with the file instead of expecting numbers: test "$(node bin/agent-canvas.mjs --info | awk '/^(Default stack versions|Compatibility):/{s=1;next} /^$/{s=0} s{print $NF}' | paste -sd' ' -)" = "$(jq -r '[.versions.agentServer,.versions.automation,.compatibility.minimumAgentServer]|join(" ")' config/defaults.json)" && echo match || echo mismatch prints match, and printf '%s\n' "$(jq -r .compatibility.minimumAgentServer config/defaults.json)" "$(jq -r .versions.agentServer config/defaults.json)" | sort -VC && echo pin-meets-minimum || echo below-minimum prints pin-meets-minimum (the default Agent Server passes the UI's own version gate).
HelpF26.cli-helpMapped behavior
Scenario F26--help · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoRun
node bin/agent-canvas.mjs --help. It printsUSAGE:,AUTH MODES:(--public ... Users must paste it when the UI loads.),OPTIONS:(-p, --port,-H, --host,--public,--frontend-only,--backend-only,-v, --version,--info,-h, --help),ENVIRONMENT VARIABLES:andEXAMPLES:, exit 0.
Expect
--help prints usage, auth modes, options, env vars and examples.
Exact CLI recipe & bug checks
Run node bin/agent-canvas.mjs --help. It prints USAGE:, AUTH MODES: (--public ... Users must paste it when the UI loads.), OPTIONS: (-p, --port, -H, --host, --public, --frontend-only, --backend-only, -v, --version, --info, -h, --help), ENVIRONMENT VARIABLES: and EXAMPLES:, exit 0.
Conflicting flagsF26.cli-flag-conflictsMapped behavior
Scenario F26--conflicting-flags · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoRun
node bin/agent-canvas.mjs --frontend-only --backend-only; echo "exit=$?"→Error: --frontend-only and --backend-only cannot be used together,exit=1. Runnode bin/agent-canvas.mjs --public --frontend-only; echo "exit=$?"→Error: --public cannot be used with --frontend-only,exit=1.
Expect
--frontend-only with --backend-only, and --public with --frontend-only, exit 1 with a one-line error before anything starts.
Exact CLI recipe & bug checks
Run node bin/agent-canvas.mjs --frontend-only --backend-only; echo "exit=$?" → Error: --frontend-only and --backend-only cannot be used together, exit=1. Run node bin/agent-canvas.mjs --public --frontend-only; echo "exit=$?" → Error: --public cannot be used with --frontend-only, exit=1.
Public mode needs a keyF26.cli-public-needs-keyKnown issue
Scenario F26--public-mode-needs-a-key · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoLaunch an isolated stack.
Expect
--public without LOCAL_BACKEND_API_KEY exits 1 with guidance.
Linked known issues
Known issue OpenHands/software-agent-sdk#5731 (F26.cli-public-needs-key): whitespace-only session keys remain configured credentials in the Agent Server. The Canvas launcher forwards a nonempty LOCAL_BACKEND_API_KEY verbatim, so a whitespace-only deployment value can leave the public login unable to use its intended key. Recheck an owned loopback-only public deployment with a whitespace-only supplied key, its actual HTTP/WebSocket authentication and a normal nonblank control; never test the server's default broad bind on an exposed interface. The SDK's missing-OH_SECRET_KEY cipher fallback is a separate configuration branch from a Canvas launcher that supplies an encryption key.
Exact CLI recipe & bug checks
Run T=$(mktemp -d); env -u LOCAL_BACKEND_API_KEY HOME=$T OH_CANVAS_SAFE_STATE_DIR=$T/state timeout 60 node bin/agent-canvas.mjs --public --port 18990; echo "exit=$?"; rm -rf $T. After ✓ uvx found it prints ✗ PUBLIC MODE requires LOCAL_BACKEND_API_KEY environment variable. and exit=1. (The successful --public launch is control-openhands launch --new --public, mapped in F01.)
Missing buildF26.cli-missing-buildMapped behavior
Scenario F26--missing-build · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoNever move this checkout's
build/(other runs serve it); copy the launcher instead:T=$(mktemp -d); mkdir -p $T/bin $T/config; cp bin/agent-canvas.mjs $T/bin/; cp package.json $T/; cp config/defaults.json $T/config/; node $T/bin/agent-canvas.mjs --port 18990; echo "exit=$?"; rm -rf $T. It printsError: No build found at <T>/build, thenpm install/npm run buildhint andexit=1.
Expect
a package without build/ exits 1 with No build found and build instructions.
Exact CLI recipe & bug checks
Never move this checkout's build/ (other runs serve it); copy the launcher instead: T=$(mktemp -d); mkdir -p $T/bin $T/config; cp bin/agent-canvas.mjs $T/bin/; cp package.json $T/; cp config/defaults.json $T/config/; node $T/bin/agent-canvas.mjs --port 18990; echo "exit=$?"; rm -rf $T. It prints Error: No build found at <T>/build, the npm install / npm run build hint and exit=1.
Port in useF26.cli-port-in-useMapped behavior
Scenario F26--port-in-use · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoPoint a second launcher at your run's ingress port:
P=$(control-openhands status | jq -r .ports.ingress); T=$(mktemp -d); HOME=$T OH_CANVAS_SAFE_STATE_DIR=$T/state timeout 60 node bin/agent-canvas.mjs --port $P; echo "exit=$?"; rm -rf $T. It printsCannot start: the following ports are already in use:with• ingress: port <P>andAnother agent-canvas instance may already be running., then exits 1. Your run is untouched (control-openhands doctorstays ok).
Expect
an occupied ingress (or service) port stops the launch with Cannot start: the following ports are already in use naming each busy port.
Exact CLI recipe & bug checks
Point a second launcher at your run's ingress port: P=$(control-openhands status | jq -r .ports.ingress); T=$(mktemp -d); HOME=$T OH_CANVAS_SAFE_STATE_DIR=$T/state timeout 60 node bin/agent-canvas.mjs --port $P; echo "exit=$?"; rm -rf $T. It prints Cannot start: the following ports are already in use: with • ingress: port <P> and Another agent-canvas instance may already be running., then exits 1. Your run is untouched (control-openhands doctor stays ok).
Loopback-only ingressF26.loopback-bind-defaultNot run
Scenario F26--loopback-only-ingress · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoYour run was started without
--host. RunP=$(control-openhands status | jq -r .ports.ingress); curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:$P/(200), then find the machine's own address portably,LAN=$(node -e 'for (const a of Object.values(require("os").networkInterfaces()).flat()) if (a.family === "IPv4" && !a.internal) { console.log(a.address); break }'); echo "LAN=$LAN", andcurl -s -o /dev/null -w '%{http_code}\n' --connect-timeout 3 http://$LAN:$P/; echo "exit=$?". At that address curl prints000andexit=7(connection refused). IfLANis empty the machine has no non-loopback address: record the loopback half and leave the refused-connection halfnot-runwith that reason (it is the behavior this ID asserts, so an emptyLANis never a pass). Read-only second view, Linux form:awk -v p=$(printf '%04X' $P) 'NR>1 && $4=="0A" && $2 ~ ":"p"$" {print $2}' /proc/net/tcpprints only0100007F:<hex port>(127.0.0.1in/proc/net/tcp's byte order; elsewhere list the listener with the OS's socket tool, for exampless -ltn 'sport = :'$Porlsof -nP -iTCP:$P -sTCP:LISTEN, and expect127.0.0.1:<P>only), andgrep -a 'Listening on' $OH_VERIFY_RUN/private/stack.logshows the ingress bannerListening on: http://localhost:<P>/. The LAN half ofF26.host-bind-lanbelow is the contrast: there the same curl answers200.
Expect
without --host the ingress listens on 127.0.0.1 only: the machine's own non-loopback address refuses the connection while loopback serves the app.
Exact CLI recipe & bug checks
Your run was started without --host. Run P=$(control-openhands status | jq -r .ports.ingress); curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:$P/ (200), then find the machine's own address portably, LAN=$(node -e 'for (const a of Object.values(require("os").networkInterfaces()).flat()) if (a.family === "IPv4" && !a.internal) { console.log(a.address); break }'); echo "LAN=$LAN", and curl -s -o /dev/null -w '%{http_code}\n' --connect-timeout 3 http://$LAN:$P/; echo "exit=$?". At that address curl prints 000 and exit=7 (connection refused). If LAN is empty the machine has no non-loopback address: record the loopback half and leave the refused-connection half not-run with that reason (it is the behavior this ID asserts, so an empty LAN is never a pass). Read-only second view, Linux form: awk -v p=$(printf '%04X' $P) 'NR>1 && $4=="0A" && $2 ~ ":"p"$" {print $2}' /proc/net/tcp prints only 0100007F:<hex port> (127.0.0.1 in /proc/net/tcp's byte order; elsewhere list the listener with the OS's socket tool, for example ss -ltn 'sport = :'$P or lsof -nP -iTCP:$P -sTCP:LISTEN, and expect 127.0.0.1:<P> only), and grep -a 'Listening on' $OH_VERIFY_RUN/private/stack.log shows the ingress banner Listening on: http://localhost:<P>/. The LAN half of F26.host-bind-lan below is the contrast: there the same curl answers 200.
Backend-onlyF26.backend-onlyMapped behavior
Scenario F26--backend-only · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoStart it:
Q=$OH_VERIFY_RUN/private/f26-backend; mkdir -p $Q; HOME=$Q/home OH_CANVAS_SAFE_STATE_DIR=$Q/state OH_CANVAS_SAFE_BACKEND_PORT=18961 OH_CANVAS_SAFE_AUTOMATION_PORT=18962 OH_CANVAS_SAFE_VITE_PORT=18963 LOCAL_BACKEND_API_KEY=qa-f26-backend-key OH_SECRET_KEY=qa-f26-secret DO_NOT_TRACK=1 nohup node bin/agent-canvas.mjs --backend-only --port 18960 > $Q/launcher.log 2>&1 & echo $! > $Q/pid. Wait untilcurl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:18960/healthis200(30–90 s; the first start downloads the agent-server into the privateHOME). Thencurl -s http://127.0.0.1:18960/printsNo backend configured for this route(status 503, also for/index.htmland/assets/x.js),/server_infois 200,/api/settingsis 401 without a key and 200 with-H 'X-Session-API-Key: qa-f26-backend-key'. Keep it running for the next two bullets.
Expect
--backend-only serves the APIs (key-protected) and answers 503 No backend configured for this route for / and static assets.
Exact CLI recipe & bug checks
Start it: Q=$OH_VERIFY_RUN/private/f26-backend; mkdir -p $Q; HOME=$Q/home OH_CANVAS_SAFE_STATE_DIR=$Q/state OH_CANVAS_SAFE_BACKEND_PORT=18961 OH_CANVAS_SAFE_AUTOMATION_PORT=18962 OH_CANVAS_SAFE_VITE_PORT=18963 LOCAL_BACKEND_API_KEY=qa-f26-backend-key OH_SECRET_KEY=qa-f26-secret DO_NOT_TRACK=1 nohup node bin/agent-canvas.mjs --backend-only --port 18960 > $Q/launcher.log 2>&1 & echo $! > $Q/pid. Wait until curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:18960/health is 200 (30–90 s; the first start downloads the agent-server into the private HOME). Then curl -s http://127.0.0.1:18960/ prints No backend configured for this route (status 503, also for /index.html and /assets/x.js), /server_info is 200, /api/settings is 401 without a key and 200 with -H 'X-Session-API-Key: qa-f26-backend-key'. Keep it running for the next two bullets.
Frontend-onlyF26.frontend-onlyMapped behavior
Scenario F26--frontend-only · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoOpen http://127.0.0.1:18970/.
Expect
--frontend-only serves the SPA; /server_info, /api/*, /sockets/* and /api/automation/* answer 503, and a browser new to that origin opens on the Add a backend onboarding step without an error toast.
Exact CLI recipe & bug checks
Start it: Q=$OH_VERIFY_RUN/private/f26-frontend; mkdir -p $Q; HOME=$Q/home OH_CANVAS_SAFE_STATE_DIR=$Q/state OH_CANVAS_SAFE_BACKEND_PORT=18971 OH_CANVAS_SAFE_AUTOMATION_PORT=18972 OH_CANVAS_SAFE_VITE_PORT=18973 DO_NOT_TRACK=1 nohup node bin/agent-canvas.mjs --frontend-only --port 18970 > $Q/launcher.log 2>&1 & echo $! > $Q/pid (ready in a few seconds). for u in / /server_info /api/settings /sockets/events/x /api/automation/v1 /vscode/; do curl -s -o /dev/null -w "%{http_code} $u\n" http://127.0.0.1:18970$u; done prints 200 / and 503 for the four backend paths and for /vscode/: the launcher serves the VS Code editor and reserves its path, and grep -a 'vscode -> 503' $Q/launcher.log prints [static] /vscode -> 503 (rejected) (see Gotchas). In the browser: control-openhands browser goto http://127.0.0.1:18970/ --allow-external, control-openhands browser value 'testid=onboarding-backend-name' (Local), control-openhands browser value 'testid=onboarding-backend-host' (http://127.0.0.1:18970, the page origin), sleep 12, control-openhands browser toasts --history ([]: no error toast) and control-openhands browser screenshot --feature F26.frontend-only --name add-backend-step (the Add a backend card).
Cross-connectF26.cross-connectMapped behavior
Scenario F26--cross-connect · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoEnter “QA Backend Only” in “Onboarding backend name”.
- DoEnter “http://127.0.0.1:18960” in “Onboarding backend host”.
- DoEnter “qa-f26-backend-key” in “Onboarding backend api key”.
- DoChoose “Onboarding backend next”.
- WaitWait for “Onboarding step choose agent” to appear.
- WaitWait for “Telemetry consent form” to appear.
- DoUse browser uncheck as specified in the exact recipe.
- DoChoose “Confirm telemetry preferences”.
- DoChoose “Onboarding skip”.
- WaitWait for “Root layout” to appear.
- DoReload the page.
- DoOpen http://127.0.0.1:18970/settings/secrets.
Expect
a frontend-only UI connects to a separate backend-only instance through Add a backend; the shell then shows that backend as Connected and all API traffic goes to it.
Exact CLI recipe & bug checks
On that page run control-openhands browser fill 'testid=onboarding-backend-name' 'QA Backend Only', control-openhands browser fill 'testid=onboarding-backend-host' 'http://127.0.0.1:18960', control-openhands browser fill 'testid=onboarding-backend-api-key' qa-f26-backend-key, control-openhands browser click 'testid=onboarding-backend-next' and control-openhands browser wait 'testid=onboarding-step-choose-agent' --timeout 20000. On a backend state that has never answered telemetry consent, the consent dialog opens on top (control-openhands browser wait 'testid=telemetry-consent-form' --timeout 10000 succeeds; it appears a second or two after the step, so an immediate browser count can still read 0; consent is stored on the backend, so a second pass with the same $Q skips it): control-openhands browser uncheck 'testid=telemetry-consent-form >> role=checkbox', control-openhands browser click 'testid=confirm-telemetry-preferences', then control-openhands browser click 'testid=onboarding-skip' and control-openhands browser wait 'testid=root-layout' --timeout 20000. After control-openhands browser reload, control-openhands browser snapshot 'testid=backend-selector' shows status "Connected" and combobox "QA Backend Only". control-openhands browser goto http://127.0.0.1:18970/settings/secrets --allow-external and control-openhands browser count 'testid=secret-item >> has-text=OPENHANDS_AUTOMATION_API_KEY' is 1 (the backend-only instance's seeded secret), and control-openhands browser network --filter 18960 --last 5 shows byOrigin with only http://127.0.0.1:18960. control-openhands browser errors --app-only has pageErrors 0.
Remote backend from a normal CanvasF26.remote-backendKnown issue
Scenario F26--remote-backend-from-a-normal-canvas · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoOpen /.
- DoChoose “Backend selector”.
- DoChoose “Add backend menu item”.
- DoChoose “Add backend option agent server”.
- DoChoose “Add backend location option remote”.
- WaitWait for “Add backend modal” to disappear.
- DoReload the page.
- WaitWait for “Telemetry consent form” to appear.
- DoUse browser uncheck as specified in the exact recipe.
- DoChoose “Confirm telemetry preferences”.
- DoChoose “Backend selector”.
- DoChoose “Local”.
- WaitWait for “Backend selector” / “Local” to appear.
- DoReload the page.
Expect
from a normal Canvas, backend selector → Add Backend → Agent-server → Remote shows the self-hosting guidance and connects to another instance (stand-in for a self-hosted VM, docs/SELF_HOSTING.md).
Linked known issues
Known issue #15434 (F26.remote-backend): The embedded VS Code action on a self-hosted reverse-proxy origin was reported to reopen Canvas because its advertised URL omitted the separate editor port/path. Current launchers include an ingress /vscode/ route; recheck against the affected HTTPS proxy deployment with an actually available editor binary and emitted URL before concluding resolution. This run lacks that self-hosted proxy/editor deployment.
Known issue OpenHands/software-agent-sdk#5373 (F26.remote-backend): A self-hosted Agent Server using default Rich traceback logging can wedge its event loop and grow memory when a websocket error has a cyclic/deep exception chain; Canvas then times out or disconnects. The npm launcher forces JSON logging, so rechecking needs a separately hosted default-Rich backend plus the reported pathological peer/backlog reproducer and bounded memory/time isolation. That original peer/driver is not included in the issue body; do not synthesize a server patch or stress the shared host and claim a live UI reproduction.
Exact CLI recipe & bug checks
Needs the backend-only instance. Run control-openhands browser goto /, control-openhands browser click 'testid=backend-selector', control-openhands browser click 'testid=add-backend-menu-item', control-openhands browser click 'testid=add-backend-option-agent-server' and control-openhands browser click 'testid=add-backend-location-option-remote'. control-openhands browser text 'testid=add-backend-agent-server-panel' includes Run the remote backend with --public, set a strong LOCAL_BACKEND_API_KEY, and expose it through an SSH tunnel, ngrok, or a TLS reverse proxy. Fill testid=add-backend-name with QA_Remote, testid=add-backend-host with http://127.0.0.1:18960 and testid=add-backend-api-key with qa-f26-backend-key, click testid=add-backend-submit, then control-openhands browser wait 'testid=add-backend-modal' --state detached --timeout 15000, control-openhands browser reload and control-openhands browser snapshot 'testid=backend-selector': status "Connected", combobox "QA_Remote" (the new backend becomes active). If you run this bullet without Cross-connect first, the backend-only state has not answered telemetry consent yet: the consent dialog (This preference is saved for the local backend “QA_Remote” at http://127.0.0.1:18960.) opens over the page and blocks the selector; answer it as in Cross-connect (control-openhands browser wait 'testid=telemetry-consent-form' --timeout 10000, control-openhands browser uncheck 'testid=telemetry-consent-form >> role=checkbox', control-openhands browser click 'testid=confirm-telemetry-preferences'). Switch back with control-openhands browser click 'testid=backend-selector' and control-openhands browser click 'role=option[name="Local"]', then control-openhands browser wait 'testid=backend-selector >> role=combobox[name="Local"]' --timeout 5000 (the switch applies after a short delay: a snapshot taken right after the click still reads combobox "QA_Remote": Local; after the wait it shows combobox "Local"). Clean up: backend-selector → testid=manage-backends-menu-item; control-openhands browser text 'testid=manage-backends-row-QA_Remote' reads QA_Remote, v<version>, http://127.0.0.1:18960, Connected, LOCAL, where <version> is the backend-only instance's own report, curl -s http://127.0.0.1:18960/server_info | jq -r .version (the versions.agentServer pin in config/defaults.json unless your shell exports an OH_AGENT_SERVER_* override, which a hand-started launcher inherits); it passes the UI's gate: printf '%s\n' "$(jq -r .compatibility.minimumAgentServer config/defaults.json)" "$(curl -s http://127.0.0.1:18960/server_info | jq -r .version)" | sort -VC && echo meets-minimum || echo below-minimum prints meets-minimum. Then click testid=manage-backends-remove-QA_Remote, testid=confirmation-modal >> testid=confirm-button, testid=manage-backends-done, browser reload.
Seeded Local backendF26.seeded-local-backendMapped behavior
Scenario F26--seeded-local-backend · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoOn your run,
control-openhands browser eval "JSON.parse(localStorage.getItem('openhands-backends')).map(b=>({id:b.id,name:b.name,host:b.host,kind:b.kind,hasKey:!!b.apiKey}))"returns exactly one entry{id: "default-local", name: "Local", host: "http://127.0.0.1:<ingress>", kind: "local", hasKey: true}(after the Remote cleanup above), andcontrol-openhands browser eval "({key: Boolean(window.__AGENT_CANVAS_SESSION_API_KEY__), authRequired: window.__AGENT_CANVAS_AUTH_REQUIRED__ === true})"is{key: true, authRequired: false}.
Expect
the first load of a launcher-served UI seeds one backend Local (id default-local) with the page origin and the injected key.
Exact CLI recipe & bug checks
On your run, control-openhands browser eval "JSON.parse(localStorage.getItem('openhands-backends')).map(b=>({id:b.id,name:b.name,host:b.host,kind:b.kind,hasKey:!!b.apiKey}))" returns exactly one entry {id: "default-local", name: "Local", host: "http://127.0.0.1:<ingress>", kind: "local", hasKey: true} (after the Remote cleanup above), and control-openhands browser eval "({key: Boolean(window.__AGENT_CANVAS_SESSION_API_KEY__), authRequired: window.__AGENT_CANVAS_AUTH_REQUIRED__ === true})" is {key: true, authRequired: false}.
Rotated session keyF26.session-key-rotatedMapped behavior
Scenario F26--rotated-session-key · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoRestart the run-owned stack as specified.
- DoReload the page.
- WaitWait for “Home screen” to appear.
Expect
in local mode a stale session key left in the browser (the launcher's key changed since the last visit) is replaced by the injected key on load: the app opens on Home without the API-key screen, and the stored Local backend holds the new key (public mode asks for it instead: F01's api-key-entry row).
Exact CLI recipe & bug checks
Still on your run's /, control-openhands browser eval "JSON.parse(localStorage.getItem('openhands-backends'))[0].apiKey === window.__AGENT_CANVAS_SESSION_API_KEY__" is true (compare keys inside the page; never print one). Run md5sum $OH_VERIFY_RUN/private/session-key | cut -c1-12, then control-openhands restart --rotate-key (rotatedKey true: the launcher comes back injecting a new key) and the md5sum line again; the hash changed. The open page still holds the old key, so its polls get 401 until it reloads: run control-openhands browser errors --clear, then control-openhands browser reload and control-openhands browser wait 'testid=home-screen' --timeout 20000. control-openhands browser count is 1 for testid=root-layout and 0 for testid=api-key-entry-screen, testid=agent-server-onboarding-screen and testid=onboarding-modal: Home, not the key prompt public mode shows in the same state (F01.api-key-entry). Second view: control-openhands browser eval "JSON.parse(localStorage.getItem('openhands-backends')).map(b=>({id:b.id,host:b.host,keyMatchesPage:b.apiKey===window.__AGENT_CANVAS_SESSION_API_KEY__}))" is [{id: "default-local", host: "http://127.0.0.1:<ingress>", keyMatchesPage: true}] against the new page key, control-openhands browser eval "JSON.parse(localStorage.getItem('openhands-agent-server-config')).sessionApiKey === window.__AGENT_CANVAS_SESSION_API_KEY__" is true (the legacy key is overwritten too), control-openhands browser network --filter 'api/settings' --last 2 shows status 200 under recent, and control-openhands browser errors --app-only has pageErrors 0 and appErrors 0. control-openhands api GET /api/settings works with the rotated key, and control-openhands browser screenshot --feature F26.session-key-rotated --name home-after-rotation shows Home.
Managed Agents bridgeF26.mars-web-bridgeKnown failure
Scenario F26--managed-agents-bridge · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoWith both partial stacks up:
curl -s "$(control-openhands status | jq -r .baseUrl)/mars/health",curl -s http://127.0.0.1:18960/mars/healthandcurl -s http://127.0.0.1:18970/mars/healtheach print{"ok":true,"authRequired":true}, whilecurl -s "http://127.0.0.1:$(control-openhands status | jq -r .ports.frontend)/mars/health" | head -c 15prints<!DOCTYPE html>(the static child behind your run's ingress has no second bridge).curl -s -o /dev/null -w '%{http_code}\n' -X POST http://127.0.0.1:18960/mars/rpc/getAuthStateis401(no key). The frontend-only page has the bridge but no key to use it: after the cross-connect,control-openhands browser eval "({marsBridge: !!window.marsBridge, key: Boolean(window.__AGENT_CANVAS_SESSION_API_KEY__)})"onhttp://127.0.0.1:18970/is{marsBridge: true, key: false}(what that does to DigitalOcean sign-in is F25'sF25.digitalocean-sign-inknown failure).
Expect
every launcher-served web stack hosts the DigitalOcean Managed Agents bridge on its ingress by default (MARS_WEB=0 opts out): /mars/health answers {"ok":true,"authRequired":true} on a full, --backend-only, --frontend-only and --host 0.0.0.0 launcher, /mars/rpc/* answers 401 without the session key and works with it, the static child behind the ingress hosts none, and the Electron stack hosts none either (its preload provides window.marsBridge).
Known bug observations
known failure).
Exact CLI recipe & bug checks
With both partial stacks up: curl -s "$(control-openhands status | jq -r .baseUrl)/mars/health", curl -s http://127.0.0.1:18960/mars/health and curl -s http://127.0.0.1:18970/mars/health each print {"ok":true,"authRequired":true}, while curl -s "http://127.0.0.1:$(control-openhands status | jq -r .ports.frontend)/mars/health" | head -c 15 prints <!DOCTYPE html> (the static child behind your run's ingress has no second bridge). curl -s -o /dev/null -w '%{http_code}\n' -X POST http://127.0.0.1:18960/mars/rpc/getAuthState is 401 (no key). The frontend-only page has the bridge but no key to use it: after the cross-connect, control-openhands browser eval "({marsBridge: !!window.marsBridge, key: Boolean(window.__AGENT_CANVAS_SESSION_API_KEY__)})" on http://127.0.0.1:18970/ is {marsBridge: true, key: false} (what that does to DigitalOcean sign-in is F25's F25.digitalocean-sign-in known failure).
Stop the partial stacksF26 · setup / cleanupSupport step
Scenario F26--stop-the-partial-stacks · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- Do
kill -TERM $(cat $OH_VERIFY_RUN/private/f26-frontend/pid) $(cat $OH_VERIFY_RUN/private/f26-backend/pid); after about 5 s the ports 18960–18963 and 18970–18973 are free again (check as in Preconditions). Kill by the saved pid only, never by pattern.
Expect
Complete this support step before continuing to the next scenario.
Exact CLI recipe & bug checks
kill -TERM $(cat $OH_VERIFY_RUN/private/f26-frontend/pid) $(cat $OH_VERIFY_RUN/private/f26-backend/pid); after about 5 s the ports 18960–18963 and 18970–18973 are free again (check as in Preconditions). Kill by the saved pid only, never by pattern.
LAN bindF26.host-bind-lanNot run
Scenario F26--lan-bind · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoStart with a fresh browser profile.
- DoOpen http://127.0.0.1:18960/.
- DoChoose “Onboarding skip”.
- WaitWait for “Api key entry screen” to appear.
- WaitWait for “Root layout” to appear.
Expect
--host 0.0.0.0 warns, listens on the machine's LAN address, and serves an index.html that carries __AGENT_CANVAS_AUTH_REQUIRED__ and neither __AGENT_CANVAS_SESSION_API_KEY__ nor the key itself (the loopback launcher's page carries the key marker); the UI behaves like --public (Add a backend with Next disabled; Skip leads to the API-key screen).
Exact CLI recipe & bug checks
Start a full stack on all interfaces: Q=$OH_VERIFY_RUN/private/f26-lan; mkdir -p $Q; HOME=$Q/home OH_CANVAS_SAFE_STATE_DIR=$Q/state OH_CANVAS_SAFE_BACKEND_PORT=18961 OH_CANVAS_SAFE_AUTOMATION_PORT=18962 OH_CANVAS_SAFE_VITE_PORT=18963 LOCAL_BACKEND_API_KEY=qa-f26-lan-key OH_SECRET_KEY=qa-f26-secret DO_NOT_TRACK=1 nohup node bin/agent-canvas.mjs --host 0.0.0.0 --port 18960 > $Q/launcher.log 2>&1 & echo $! > $Q/pid, and wait until curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:18960/ is 200. grep -a 'not loopback' $Q/launcher.log shows [auth] Bind host 0.0.0.0 is not loopback — session key will not be injected into HTML and [static] WARNING: bind host 0.0.0.0 is not loopback; refusing to inject the session API key into HTML. The served page proves it: curl -s http://127.0.0.1:18960/ | grep -o '__AGENT_CANVAS_\(SESSION_API_KEY\|AUTH_REQUIRED\)__' | sort -u prints only __AGENT_CANVAS_AUTH_REQUIRED__ and curl -s http://127.0.0.1:18960/ | grep -c qa-f26-lan-key prints 0, while your loopback run's page, curl -s "$(control-openhands status | jq -r .baseUrl)/" | grep -o '__AGENT_CANVAS_\(SESSION_API_KEY\|AUTH_REQUIRED\)__' | sort -u, prints only __AGENT_CANVAS_SESSION_API_KEY__. The bind is real: with LAN from the loopback bullet, curl -s -o /dev/null -w '%{http_code}\n' --connect-timeout 3 http://$LAN:18960/ is 200 here (refused on your run, F26.loopback-bind-default; with an empty LAN this check is not-run). The browser part below expects a profile that has never visited 127.0.0.1:18960 (true in this file's order; on a repeat pass run control-openhands browser reset first and restore your run afterwards, see Gotchas). Then control-openhands browser goto http://127.0.0.1:18960/ --allow-external and control-openhands browser eval "({key: Boolean(window.__AGENT_CANVAS_SESSION_API_KEY__), authRequired: window.__AGENT_CANVAS_AUTH_REQUIRED__ === true})" → {key: false, authRequired: true}. The onboarding shows testid=onboarding-step-check-backend with control-openhands browser enabled 'testid=onboarding-backend-next' false (no key typed). control-openhands browser click 'testid=onboarding-skip' and control-openhands browser wait 'testid=api-key-entry-screen' --timeout 15000 succeed; after sleep 12, control-openhands browser toasts --history is [] (no error toast on either screen); the bridge is mounted behind the key (F26.mars-web-bridge): curl -s http://127.0.0.1:18960/mars/health is {"ok":true,"authRequired":true}, and POST /mars/rpc/getAuthState with -H 'Content-Type: application/json' -d '{"args":[]}' is 401 without and 200 with -H 'X-Session-API-Key: qa-f26-lan-key'; control-openhands browser screenshot --feature F26.host-bind-lan --name api-key-screen. Fill testid=api-key-entry-name with QA_LAN and testid=api-key-entry-api-key with qa-f26-lan-key, click testid=api-key-entry-submit; control-openhands browser wait 'testid=root-layout' --timeout 10000 succeeds. A few seconds later the telemetry consent dialog for the new QA_LAN backend opens and blocks clicks; answer it (role=button[name="Confirm preferences"]) before driving that page further. Stop it with kill -TERM $(cat $Q/pid).
LAN opt-inF26.host-bind-lan-optinKnown failure
Scenario F26--lan-opt-in · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoExpected: the opt-in the warning names (
Pass --host 127.0.0.1 (default) for local mode, or --allow-lan-session-key only if you accept LAN exposure.) makes the npm launcher inject the key again. Start the LAN stack as above with--host 0.0.0.0 --allow-lan-session-key --port 18960(log to$Q/launcher2.log), wait for200, thencurl -s http://127.0.0.1:18960/ | grep -o '__AGENT_CANVAS_\(SESSION_API_KEY\|AUTH_REQUIRED\)__' | sort -u. Today it prints only__AGENT_CANVAS_AUTH_REQUIRED__and the log repeats both "not loopback" warnings: the launcher ignores the flag (fail; see Gotchas). Stop it withkill -TERM $(cat $Q/pid).
Expect
the opt-in the launcher's warning recommends (--allow-lan-session-key) should restore key injection on a LAN bind.
Known bug observations
Known failure (reproduced 2026-10-08): the npm launcher ignores the flag and the page still carries only __AGENT_CANVAS_AUTH_REQUIRED__; #17949.
Exact CLI recipe & bug checks
Expected: the opt-in the warning names (Pass --host 127.0.0.1 (default) for local mode, or --allow-lan-session-key only if you accept LAN exposure.) makes the npm launcher inject the key again. Start the LAN stack as above with --host 0.0.0.0 --allow-lan-session-key --port 18960 (log to $Q/launcher2.log), wait for 200, then curl -s http://127.0.0.1:18960/ | grep -o '__AGENT_CANVAS_\(SESSION_API_KEY\|AUTH_REQUIRED\)__' | sort -u. Today it prints only __AGENT_CANVAS_AUTH_REQUIRED__ and the log repeats both "not loopback" warnings: the launcher ignores the flag (fail; see Gotchas). Stop it with kill -TERM $(cat $Q/pid).
Generated session key persistsF26.session-key-persistMapped behavior
Scenario F26--generated-session-key-persists · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoWith 18960–18963 free, define a helper and start a loopback stack without a key:
Q=$OH_VERIFY_RUN/private/f26-keys; mkdir -p $Q; start(){ HOME=$Q/home OH_CANVAS_SAFE_STATE_DIR=$Q/state OH_CANVAS_SAFE_BACKEND_PORT=18961 OH_CANVAS_SAFE_AUTOMATION_PORT=18962 OH_CANVAS_SAFE_VITE_PORT=18963 OH_SECRET_KEY=qa-f26-secret DO_NOT_TRACK=1 "$@" > $Q/launcher.log 2>&1 & echo $! > $Q/pid; for i in $(seq 1 60); do c=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:18960/); [ "$c" = 200 ] && break; sleep 3; done; echo "code=$c"; }; start env -u LOCAL_BACKEND_API_KEY nohup node bin/agent-canvas.mjs --port 18960(code=200).curl -s http://127.0.0.1:18960/ | grep -o '__AGENT_CANVAS_SESSION_API_KEY__[^;<]*' | md5sumprints a hash (never print the key itself);$Q/home/.openhands/agent-canvas/api-key.txtexists (64 hex characters) andcurl -s -o /dev/null -w '%{http_code}' -H "X-Session-API-Key: $(cat $Q/home/.openhands/agent-canvas/api-key.txt)" http://127.0.0.1:18960/api/settingsis200.kill -TERM $(cat $Q/pid); sleep 6, run the samestart ...line again in the same shell and repeat themd5sumline: the hash is identical.
Expect
in local mode without LOCAL_BACKEND_API_KEY the launcher generates a session key, saves it under $HOME/.openhands/agent-canvas/api-key.txt and injects the same key again on the next start.
Exact CLI recipe & bug checks
With 18960–18963 free, define a helper and start a loopback stack without a key: Q=$OH_VERIFY_RUN/private/f26-keys; mkdir -p $Q; start(){ HOME=$Q/home OH_CANVAS_SAFE_STATE_DIR=$Q/state OH_CANVAS_SAFE_BACKEND_PORT=18961 OH_CANVAS_SAFE_AUTOMATION_PORT=18962 OH_CANVAS_SAFE_VITE_PORT=18963 OH_SECRET_KEY=qa-f26-secret DO_NOT_TRACK=1 "$@" > $Q/launcher.log 2>&1 & echo $! > $Q/pid; for i in $(seq 1 60); do c=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:18960/); [ "$c" = 200 ] && break; sleep 3; done; echo "code=$c"; }; start env -u LOCAL_BACKEND_API_KEY nohup node bin/agent-canvas.mjs --port 18960 (code=200). curl -s http://127.0.0.1:18960/ | grep -o '__AGENT_CANVAS_SESSION_API_KEY__[^;<]*' | md5sum prints a hash (never print the key itself); $Q/home/.openhands/agent-canvas/api-key.txt exists (64 hex characters) and curl -s -o /dev/null -w '%{http_code}' -H "X-Session-API-Key: $(cat $Q/home/.openhands/agent-canvas/api-key.txt)" http://127.0.0.1:18960/api/settings is 200. kill -TERM $(cat $Q/pid); sleep 6, run the same start ... line again in the same shell and repeat the md5sum line: the hash is identical.
Pinned session keyF26.session-key-pinnedMapped behavior
Scenario F26--pinned-session-key · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoOpen http://127.0.0.1:18960/.
Expect
LOCAL_BACKEND_API_KEY=<key> in local mode injects exactly that key into the page and the API accepts only it (the saved generated key gets 401).
Exact CLI recipe & bug checks
Same shell, after kill -TERM $(cat $Q/pid); sleep 6: start env LOCAL_BACKEND_API_KEY=qa-f26-pin-key nohup node bin/agent-canvas.mjs --port 18960. curl -s http://127.0.0.1:18960/ | grep -o '__AGENT_CANVAS_SESSION_API_KEY__[^;<]*' prints __AGENT_CANVAS_SESSION_API_KEY__="qa-f26-pin-key"; /api/settings is 200 with -H 'X-Session-API-Key: qa-f26-pin-key' and 401 with the saved generated key. In the browser, control-openhands browser goto http://127.0.0.1:18960/ --allow-external and control-openhands browser eval "({key: window.__AGENT_CANVAS_SESSION_API_KEY__ === 'qa-f26-pin-key', authRequired: window.__AGENT_CANVAS_AUTH_REQUIRED__ === true})" → {key: true, authRequired: false}.
Bind host from the environmentF26.host-bind-envMapped behavior
Scenario F26--bind-host-from-the-environment · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoSame shell, after
kill -TERM $(cat $Q/pid); sleep 6:start env LOCAL_BACKEND_API_KEY=qa-f26-pin-key OH_BIND_HOST=0.0.0.0 nohup node bin/agent-canvas.mjs --port 18960.grep -a 'not loopback' $Q/launcher.logshows the same[auth]and[static]warnings as--host 0.0.0.0, andcurl -s http://127.0.0.1:18960/ | grep -o '__AGENT_CANVAS_\(SESSION_API_KEY\|AUTH_REQUIRED\)__' | sort -uprints only__AGENT_CANVAS_AUTH_REQUIRED__. Stop it withkill -TERM $(cat $Q/pid); after about 6 s ports 18960–18963 are free.
Expect
OH_BIND_HOST=0.0.0.0 (the environment form of --host) prints the same two "not loopback" warnings and does not inject the session key, even with LOCAL_BACKEND_API_KEY set.
Exact CLI recipe & bug checks
Same shell, after kill -TERM $(cat $Q/pid); sleep 6: start env LOCAL_BACKEND_API_KEY=qa-f26-pin-key OH_BIND_HOST=0.0.0.0 nohup node bin/agent-canvas.mjs --port 18960. grep -a 'not loopback' $Q/launcher.log shows the same [auth] and [static] warnings as --host 0.0.0.0, and curl -s http://127.0.0.1:18960/ | grep -o '__AGENT_CANVAS_\(SESSION_API_KEY\|AUTH_REQUIRED\)__' | sort -u prints only __AGENT_CANVAS_AUTH_REQUIRED__. Stop it with kill -TERM $(cat $Q/pid); after about 6 s ports 18960–18963 are free.
Frontend-only, returning userF26.frontend-only-returningKnown failure
Scenario F26--frontend-only-returning-user · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoStart with a fresh browser profile.
- DoOpen http://127.0.0.1:18960/.
- WaitWait for “Telemetry consent form” to appear.
- DoUse browser uncheck as specified in the exact recipe.
- DoChoose “Confirm telemetry preferences”.
- WaitWait for “Onboarding step choose agent” to appear.
- DoChoose “Onboarding skip”.
- WaitWait for “Root layout” to appear.
- DoOpen http://127.0.0.1:18960/.
- WaitWait for “Agent server onboarding screen” to appear.
- DoPress Escape.
- DoOpen /.
- DoFollow the onboarding setup in the recipe.
Expect
on a frontend-only origin, a browser that already stores a backend for that origin (the default-local entry a full launcher on the same port seeded, or one added there earlier) gets the recovery gate instead of onboarding: agent-server-onboarding-screen with Manage backends in recovery mode and the Local row Disconnected with the 503 detail (the gate itself is F25's recovery-gate row); the failing probes raise generic An error occurred toasts there (
Known bug observations
Known failure, reproduced 2026-10-08: #18160).
Linked known issues
Which screen a frontend-only origin shows depends on the browser, not the launcher: a profile new to the origin gets Add a backend (first run owns the initial backend), while a profile that stores a backend for that origin (the default-local entry a full launcher on the same port seeded, or any backend added there earlier) gets the recovery gate of F25.recovery-gate as soon as that backend answers 503 (F26.frontend-only-returning). The no-toast check belongs to the first-run state only: on the gate, browser toasts --history lists generic An error occurred status toasts raised by the failing 503 probes (two within 12 s here), with no hint of which request failed. Known failure (reproduced 2026-10-08): both toasts come from the free-models hydrator's GET /api/llm/models/verified 503, which no meta.disableToast guards on that screen (nor does backend-version.tsx); #18160.
Exact CLI recipe & bug checks
Same shell, after kill -TERM $(cat $Q/pid); sleep 6. Arrange a browser that onboarded on this origin against the full launcher: start env LOCAL_BACKEND_API_KEY=qa-f26-pin-key nohup node bin/agent-canvas.mjs --port 18960 (code=200), control-openhands browser reset (a fresh profile, which also forgets your run's onboarding: restore it at the end), control-openhands browser goto http://127.0.0.1:18960/ --allow-external, control-openhands browser wait 'testid=telemetry-consent-form' --timeout 15000 (this backend state has never answered consent; a second pass on the same $Q skips the form), control-openhands browser uncheck 'testid=telemetry-consent-form >> role=checkbox', control-openhands browser click 'testid=confirm-telemetry-preferences', control-openhands browser wait 'testid=onboarding-step-choose-agent' --timeout 15000, control-openhands browser click 'testid=onboarding-skip' and control-openhands browser wait 'testid=root-layout' --timeout 20000. control-openhands browser eval "JSON.parse(localStorage.getItem('openhands-backends')).map(b=>({id:b.id,name:b.name,host:b.host}))" is [{id: "default-local", name: "Local", host: "http://127.0.0.1:18960"}]. Now serve the same port without backends: kill -TERM $(cat $Q/pid); sleep 6; start nohup node bin/agent-canvas.mjs --frontend-only --port 18960 (code=200, and curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:18960/server_info is 503). control-openhands browser goto http://127.0.0.1:18960/ --allow-external and control-openhands browser wait 'testid=agent-server-onboarding-screen' --timeout 20000 succeed: the gate of F25.recovery-gate, not onboarding. control-openhands browser count is 1 for testid=manage-backends-modal and 0 for testid=onboarding-step-check-backend, testid=first-run-onboarding-screen, testid=api-key-entry-screen, testid=root-layout, testid=close-manage-backends-modal and testid=manage-backends-done; control-openhands browser press Escape leaves the modal (count 1). control-openhands browser text 'testid=manage-backends-row-Local' reads Local, http://127.0.0.1:18960, Disconnected, HTTP request failed (503 Service Unavailable): "Service Unavailable (no backend configured for this route)" and LOCAL, and control-openhands browser testids lists manage-backends-add (Add Backend) as the way on. control-openhands browser screenshot --feature F26.frontend-only-returning --name recovery-gate. The 503 probes are expected errors in the sweep: control-openhands browser errors (without --app-only, which keeps only your run's origin) lists external:http-error rows with 503 for http://127.0.0.1:18960/server_info, /api/settings and /api/llm/models/verified, and pageErrors is 0. Stop it with kill -TERM $(cat $Q/pid); after about 6 s ports 18960–18963 are free. Restore your run: control-openhands browser goto / and control-openhands onboard --skip.
Automation exits during startupF26.automation-startup-failureKnown failure
Scenario F26--automation-exits-during-startup · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoWith 18960–18963 free, start a loopback launcher whose automation cannot install:
Q=$OH_VERIFY_RUN/private/f26-autofail; mkdir -p $Q; HOME=$Q/home OH_CANVAS_SAFE_STATE_DIR=$Q/state OH_CANVAS_SAFE_BACKEND_PORT=18961 OH_CANVAS_SAFE_AUTOMATION_PORT=18962 OH_CANVAS_SAFE_VITE_PORT=18963 OH_AUTOMATION_VERSION=0.0.1 LOCAL_BACKEND_API_KEY=qa-f26-autofail-key OH_SECRET_KEY=qa-f26-secret DO_NOT_TRACK=1 nohup node bin/agent-canvas.mjs --port 18960 > $Q/launcher.log 2>&1 & echo $! > $Q/pid, then poll the log untilMain UIappears or the pid exits. Expected (#18362): noMain UIbanner, a message naming the automation startup failure, a nonzero exit and closed ports. Known failure (reproduced 2026-10-13 twice, with0.0.1and0.0.0-qa-missing): the log shows[automation] ... No solution found ...and[automation] Exited with code 1(code 2 for the unparsable version), thenMain UI: http://localhost:18960/two seconds later;kill -0 $(cat $Q/pid)succeeds,curl http://127.0.0.1:18960/is200and/api/automation/health(with the key) is502. Stop it withkill -TERM $(cat $Q/pid); the ports free within seconds. The #18299 migration failure in the issue is the same path through a different exit cause.
Expect
when the requested automation backend exits before it is ready, the launcher should print no success banner, stop the services it started and exit nonzero with the automation's exit reason.
Known bug observations
Known failure (reproduced 2026-10-13 twice, with 0.0.1 and 0.0.0-qa-missing): the log shows [automation] ... No solution found ... and [automation] Exited with code 1 (code 2 for the unparsable version), then Main UI: http://localhost:18960/ two seconds later; kill -0 $(cat $Q/pid) succeeds, curl http://127.0.0.1:18960/ is 200 and /api/automation/health (with the key) is 502. Stop it with kill -TERM $(cat $Q/pid); the ports free within seconds. The #18299 migration failure in the issue is the same path through a different exit cause.
Known failure (reproduced 2026-10-13, #18362): it prints the Main UI banner and keeps the stack up with /api/automation/health answering 502.
Exact CLI recipe & bug checks
With 18960–18963 free, start a loopback launcher whose automation cannot install: Q=$OH_VERIFY_RUN/private/f26-autofail; mkdir -p $Q; HOME=$Q/home OH_CANVAS_SAFE_STATE_DIR=$Q/state OH_CANVAS_SAFE_BACKEND_PORT=18961 OH_CANVAS_SAFE_AUTOMATION_PORT=18962 OH_CANVAS_SAFE_VITE_PORT=18963 OH_AUTOMATION_VERSION=0.0.1 LOCAL_BACKEND_API_KEY=qa-f26-autofail-key OH_SECRET_KEY=qa-f26-secret DO_NOT_TRACK=1 nohup node bin/agent-canvas.mjs --port 18960 > $Q/launcher.log 2>&1 & echo $! > $Q/pid, then poll the log until Main UI appears or the pid exits. Expected (#18362): no Main UI banner, a message naming the automation startup failure, a nonzero exit and closed ports. Known failure (reproduced 2026-10-13 twice, with 0.0.1 and 0.0.0-qa-missing): the log shows [automation] ... No solution found ... and [automation] Exited with code 1 (code 2 for the unparsable version), then Main UI: http://localhost:18960/ two seconds later; kill -0 $(cat $Q/pid) succeeds, curl http://127.0.0.1:18960/ is 200 and /api/automation/health (with the key) is 502. Stop it with kill -TERM $(cat $Q/pid); the ports free within seconds. The #18299 migration failure in the issue is the same path through a different exit cause.
Runtime servicesF26.runtime-servicesKnown issue
Scenario F26--runtime-services · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- SetupFollow the environment or fixture command in the exact recipe.
- DoStart a conversation with the prompt “Reply with the single word: ok”.
Expect
/server_info.runtime_services lists the agent-server, ingress, frontend and automation URLs as the agent sees them, and a new conversation's system prompt carries a matching <RUNTIME_SERVICES> block.
Linked known issues
Known issue #16720 (F26.runtime-services): CPU-heavy commands in several conversations can starve Agent Server and stop the UI loading; the report requests lower priority for agent commands than the service. Recheck with a disposable bounded workload, recorded CPU/resource limits and real command observations, never arbitrary stress on the shared machine. The exact Rust workload and affected resource envelope are missing, and real agent execution needs a model key. The backend counterpart is OpenHands/software-agent-sdk#4575.
Known issue #16690 (F26.docker-image F26.runtime-services): A Docker deployment with nine chats becomes slow/disconnected, then reloads lose the list and startup proxies report ECONNREFUSED. Recheck on the affected Windows-host Docker configuration with retained conversation state and a controlled workload; creating a few empty local chats cannot prove the reported resource/runtime failure. This run lacks Docker and the original conversation/workload fixture.
Known issue #16300 (F26.runtime-services): The npm launcher can fail while building LiteLLM native dependencies when resolved AWS crates require a newer Rust compiler, leaving backend services unavailable. Recheck an isolated uncached uv environment with litellm==1.95.0, the reported Rust 1.93.1/toolchain/platform and dependency resolution; a warm current backend cache cannot exercise that install failure. The historical dependency/toolchain fixture is unavailable.
Known issue #16208 (F26.runtime-services): On Fedora Rawhide/Python 3.14.6, settings work but starting any conversation returns HTTP 500. Recheck a native Fedora environment with the reported SDK/runtime and model/profile, including the underlying server traceback; this macOS current-runtime stack cannot prove that platform-specific failure.
Known issue OpenHands/software-agent-sdk#4901 (F26.runtime-services): closing an ACP conversation on POSIX is reported to leave the adapter's descendant Node/CLI processes alive after the top npm process exits. Recheck an owned authenticated ACP provider command chain, record its exact descendant identities before close and inspect only those identities afterwards; cleanup must target those owned groups. The current run has no authorized authenticated ACP runtime for this path.
Known issue OpenHands/software-agent-sdk#5974 (F26.runtime-services; workspace picker in F03): a saved-workspace mutation waiting for its registry file lock can block the Agent Server event loop, including /alive, until the lock is released. Recheck only an owned registry using a bounded lock holder, an independently confirmed waiting mutation, concurrent liveness probes and a successful post-release readback; the original evidence uses Linux kernel lock-wait readback. A healthy idle server or concurrent writes without deliberate lock contention do not cover it.
Exact CLI recipe & bug checks
Run control-openhands api GET /server_info --pick runtime_services.mode (agent-canvas) and control-openhands api GET /server_info --pick runtime_services.services.automation.url_from_agent (http://localhost:<automation port>; control-openhands status prints the ports). Then control-openhands conversation start --prompt "Reply with the single word: ok" --wait --timeout 180 (prints <id>) and control-openhands conversation events <id> --grep RUNTIME_SERVICES --from-start: the SystemPromptEvent matches, with an excerpt <RUNTIME_SERVICES>\nYou are running inside an agent-canvas dev stack started .... control-openhands conversation events <id> --grep "localhost:<automation port>" --from-start shows * Automation backend: http://localhost:<port> plus its Docs: and OpenAPI: URLs.
The agent uses the runtime servicesF26.runtime-services-agent-useMapped behavior
Scenario F26--the-agent-uses-the-runtime-services · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoStart a conversation with the prompt “$(cat qa-f26-agent.txt)”.
- DoOpen /automations.
- DoChoose the element identified in the CLI recipe.
- SetupArrange the prerequisite backend state through the API.
Expect
with that block alone, the agent can reach the automation backend from its terminal: asked to, it reads the OpenAPI at the URL the block names, creates an enabled cron automation, and dispatches a run that completes; the automation and its run show up in the Automations UI.
Exact CLI recipe & bug checks
Write qa-f26-agent.txt (outside the workspace) containing: Using the Automation backend described in your RUNTIME_SERVICES (read its OpenAPI to find the routes), create one automation named QA_rt_auto from a prompt: the prompt is "Reply with the single word: pong. Do not run any tools.", the schedule is the cron 0 9 * * *, and it is enabled. Then dispatch one run of it now. Do not change anything else. Reply with the automation id and the run id only. Run control-openhands conversation start --prompt "$(cat qa-f26-agent.txt)" --wait --timeout 400 (note <agent-id>); the reply names <automation-id> and a run id. control-openhands conversation events <agent-id> --kinds ActionEvent --from-start shows the agent's own curl calls: http://localhost:<automation port>/api/automation/openapi.json, POST …/api/automation/v1/preset/prompt and POST …/api/automation/v1/<automation-id>/dispatch, each with the X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY header the block names. Second view in the UI: control-openhands browser goto /automations and control-openhands browser text '[data-testid^=automation-card-] >> has-text=QA_rt_auto' (QA_rt_auto, the prompt, cron, Runs 1). Open it with control-openhands browser click '[data-testid^=automation-card-] >> has-text=QA_rt_auto >> text=QA_rt_auto' --expect-url '/automations/[0-9a-f-]+'. Within about a minute, control-openhands browser text 'testid=automation-activity-log' shows the run's summary (Replied with the single word "pong" …), a cost and Successful (control-openhands browser screenshot --feature F26.runtime-services-agent-use --name agent-created-automation-run). control-openhands api GET /api/automation/v1/<automation-id> has name QA_rt_auto, trigger.schedule 0 9 * * * and enabled true. Delete it afterwards (control-openhands api DELETE /api/automation/v1/<automation-id> --write, arrange). It runs daily at 09:00 UTC otherwise.
Library buildF26.lib-buildMapped behavior
Scenario F26--library-build · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoNever build inside the shared checkout (it regenerates i18n and typegen files the other runs hash); build in a copy:
L=$OH_VERIFY_RUN/private/f26-lib; mkdir -p $L; tar --exclude=./node_modules --exclude=./build --exclude=./.git --exclude=./dist -cf - . | tar -xf - -C $L; ln -s "$PWD/node_modules" $L/node_modules; (cd $L && npm run build:lib); echo "exit=$?". It ends with✓ built inandexit=0(about 30 s). Then(cd $L && node -e 'const p=require("./package.json"),fs=require("fs");let ok=0,miss=[];for(const[k,v]of Object.entries(p.exports))for(const f of(typeof v==="string"?[v]:Object.values(v)))fs.existsSync(f)?ok++:miss.push(k+" -> "+f);console.log(JSON.stringify({entries:Object.keys(p.exports),ok,missing:miss}))')prints 9 entries (.,./browser,./conversation,./files,./settings,./sidebar,./terminal,./i18n,./package.json),ok25 andmissing[].grep -ohE 'AgentServerUIProviders|AgentServerUIRoot|CloudOrganizationBoundary|configureTelemetry|setTelemetryConsent' $L/dist/lib/index.d.ts | sort -ulists all five. Remove the copy withrm -rf $L.
Expect
npm run build:lib produces dist/ where every package.json exports target exists and the main entry exports AgentServerUIProviders, AgentServerUIRoot, CloudOrganizationBoundary and the telemetry helpers.
Exact CLI recipe & bug checks
Never build inside the shared checkout (it regenerates i18n and typegen files the other runs hash); build in a copy: L=$OH_VERIFY_RUN/private/f26-lib; mkdir -p $L; tar --exclude=./node_modules --exclude=./build --exclude=./.git --exclude=./dist -cf - . | tar -xf - -C $L; ln -s "$PWD/node_modules" $L/node_modules; (cd $L && npm run build:lib); echo "exit=$?". It ends with ✓ built in and exit=0 (about 30 s). Then (cd $L && node -e 'const p=require("./package.json"),fs=require("fs");let ok=0,miss=[];for(const[k,v]of Object.entries(p.exports))for(const f of(typeof v==="string"?[v]:Object.values(v)))fs.existsSync(f)?ok++:miss.push(k+" -> "+f);console.log(JSON.stringify({entries:Object.keys(p.exports),ok,missing:miss}))') prints 9 entries (., ./browser, ./conversation, ./files, ./settings, ./sidebar, ./terminal, ./i18n, ./package.json), ok 25 and missing []. grep -ohE 'AgentServerUIProviders|AgentServerUIRoot|CloudOrganizationBoundary|configureTelemetry|setTelemetryConsent' $L/dist/lib/index.d.ts | sort -u lists all five. Remove the copy with rm -rf $L.
Style scopeF26.lib-style-scopeMapped behavior
Scenario F26--style-scope · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoOpen /.
Expect
all bundled CSS is scoped under [data-agent-server-ui], and theme tokens such as --oh-color-base live on that scope root (observable in the standalone app).
Exact CLI recipe & bug checks
On your run's home page (control-openhands browser goto /) run control-openhands browser eval "(() => { let total=0, unscoped=[]; for (const sh of document.styleSheets) { let rules; try { rules = sh.cssRules } catch { continue } const walk = (rs) => { for (const r of rs) { if (r.selectorText) { total++; if (!r.selectorText.includes('data-agent-server-ui')) unscoped.push(r.selectorText) } else if (r.cssRules) walk(r.cssRules) } }; walk(rules) } return { scopeRoots: document.querySelectorAll('[data-agent-server-ui]').length, total, unscopedCount: unscoped.length, sample: unscoped.slice(0, 12) } })()". total is in the thousands and unscopedCount is 2: only .go<hash> and .go<hash> > *, react-hot-toast's runtime classes. control-openhands browser eval "getComputedStyle(document.querySelector('[data-agent-server-ui]')).getPropertyValue('--oh-color-base').trim()" is #181818. This proves the bundle is scoped, not that a host page is unaffected (F26.lib-host-app).
BlockedF26.lib-host-app · F26.docker-image · F26.docker-conversation-runtime · F26.helm-chartKnown issueBlocked
Scenario F26--blocked · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoIf
docker infocannot reach the daemon butdockerdis installed, start it (setsid dockerd); without one, record both Docker rowsblockednaming the missing daemon. With a daemon, run the image with-p 127.0.0.1:8000:8000, openhttp://localhost:8000/canvasand expect the Add a backend step (testid=onboarding-step-check-backend, Next disabled until a key is typed);onboarding-skipleads totestid=api-key-entry-screen. With-e AGENT_CANVAS_ALLOW_LAN_SESSION_KEY=truethe key is injected and first run opens on Choose your agent, under the telemetry consent dialog.npm run build:dockerbehind a TLS-intercepting proxy needs the proxy CA inside the build stages (without ituv pip install/pip installin the final stage fail withCERTIFICATE_VERIFY_FAILED: self-signed certificate in certificate chain), and Docker Hub may answer 429. Build a scratch copy of the Dockerfile that only adds the CA, never an editeddocker/Dockerfile: afterFROM node:24-slim AS frontend-buildaddCOPY --from=ccrca ca-bundle.crt /tmp/qa-ca.crtandENV NODE_EXTRA_CA_CERTS=/tmp/qa-ca.crt npm_config_cafile=/tmp/qa-ca.crt, after the firstUSER rootadd the sameCOPYandENV SSL_CERT_FILE=/tmp/qa-ca.crt PIP_CERT=/tmp/qa-ca.crt REQUESTS_CA_BUNDLE=/tmp/qa-ca.crt UV_NATIVE_TLS=1, thendocker build -f <copy> --build-context ccrca=<dir holding ca-bundle.crt> --network host --build-arg AGENT_SERVER_IMAGE=<images.agentServer>:<versions.agentServer>-python --build-arg AGENT_SERVER_VERSION=<versions.agentServer> --build-arg AUTOMATION_VERSION=<versions.automation> --build-arg VITE_BASE_PATH=/canvas -t qa-agent-canvas:target .(the pinsscripts/docker-build.mjsreads fromconfig/defaults.json; about 5 min on a warm cache). Publish it on a free port (-p 127.0.0.1:<free>:8000; 8000 and the 188xx blocks may belong to other runs). Docker Hub may answer429 Too Many Requestsfornode:24-slim; in the scratch copy only, replace bothFROM node:24-slimlines withFROM mirror.gcr.io/library/node:24-slim(arrange; say so in the note). Run the unmodified image withdocker run -d --name <qa-name> -p 127.0.0.1:<free>:8000 qa-agent-canvas:target. On a kernel without IPv6,docker logs <qa-name>showsIPv6 is not available; binding the frontend to 0.0.0.0.andStatic-server + proxy listening on http://0.0.0.0:8000/canvas/,/canvas/answers200within about 25 s anddocker inspect -f '{{.State.Status}}' <qa-name>is stillrunningminutes later (#18178 fixed by #18180, verified 2026-10-13);docker exec <qa-name> ls /opt/agent-canvaslists themars-*.mjsbridge modules (#18343). The container's generated key isdocker exec <qa-name> cat /home/openhands/.openhands/agent-canvas/api-key.txt(write it to$OH_VERIFY_RUN/private/, fill with--value-file). Settings → Application (/canvas/settings/app) shows the image'sVersion(agent-canvas-update-card), which matchesjq -r .version package.json. Remove the containers and the image afterwards (docker rm -f,docker rmi). RecordF26.lib-host-appblockednaming the missing host-app example.command -v helm kubectlprints nothing here: recordF26.helm-chartblockednaming the missinghelmbinary and cluster; with them,helm lint helm/agent-canvasandhelm template qa helm/agent-canvasrender the StatefulSet, Service and Ingress, andhelm installfollowshelm/agent-canvas/README.md.
Expect
mounted in a separate host app, Canvas styles stay inside the scope and styleOverrides restyle it. Blocked: no host-app example exists.
docker run ghcr.io/openhands/agent-canvas serves Canvas at /canvas; without AGENT_CANVAS_ALLOW_LAN_SESSION_KEY=true the UI asks for the key. Blocked without a Docker daemon. On a host whose kernel has no IPv6 (no /proc/net/if_inet6) the entrypoint logs IPv6 is not available; binding the frontend to 0.0.0.0. and the container keeps running (#18180).
OH_CONVERSATION_RUNTIME=docker runs each new conversation in its own container. Blocked without a Docker daemon.
helm install agent-canvas ./helm/agent-canvas runs the Docker image as a Kubernetes StatefulSet with a PVC and an Ingress. Blocked without helm and a cluster.
Linked known issues
Known issue #17763 (F26.docker-image): The Docker entrypoint can leave protected Agent Server APIs unauthenticated when only LOCAL_BACKEND_API_KEY is supplied, because its export to OH_SESSION_API_KEYS_0 is inside the generate-key branch. Recheck an owned container with a dummy custom key: unauthenticated GET /api/conversations/search must be 401 and the same request with that dummy key 200. /server_info is intentionally public and is not an auth probe. Blocked here without a Docker daemon.
Known issue #17623 (F26.helm-chart): The chart defaults service-account-token automounting to true even with RBAC disabled, exposing an unused cluster credential to agent processes. Recheck a disposable Kubernetes installation using the default values and an explicit opt-in; inspect token existence/readability only, never its contents. Blocked without Helm and a Kubernetes cluster.
Known issue #17470 (F26.docker-image): On Apple Silicon under Colima/Virtualization.framework, the image's Python services can exit with SIGILL while the frontend leaves onboarding at Disconnected. The report isolates import litellm and compares OPENSSL_armcap=0; recheck on the affected ARM/Colima guest with the reported cryptography/OpenSSL combination. A native macOS npm stack is not that container environment.
Known issue #16690 (F26.docker-image F26.runtime-services): A Docker deployment with nine chats becomes slow/disconnected, then reloads lose the list and startup proxies report ECONNREFUSED. Recheck on the affected Windows-host Docker configuration with retained conversation state and a controlled workload; creating a few empty local chats cannot prove the reported resource/runtime failure. This run lacks Docker and the original conversation/workload fixture.
Known issue #16511 (F26.docker-image): The documented Docker bind mount can be unwritable to container uid 10001, killing Agent Server/automation while the frontend still serves Bad Gateway. Recheck a disposable host-owned mode-700 mount and a writable control against an owned image; never chmod the operator's real state directory. Blocked here without Docker.
Known issue OpenHands/software-agent-sdk#5310 (F26.docker-conversation-runtime): A Docker conversation can lose the auxiliary title profile at the runtime boundary, leaving an ACP conversation untitled or producing an unrelated title-auth error while the agent still works. Recheck a saved reachable title profile and an ACP conversation inside an owned per-conversation Docker runtime, comparing its title/profile metadata with the host; inspect only profile names and credential-presence booleans. Blocked without Docker and real auxiliary-title/ACP credentials.
Known issue OpenHands/software-agent-sdk#5307 (F26.docker-conversation-runtime): New worktree on a Docker-backed conversation can create git metadata whose reverse path exists only inside the container, so the still-live host worktree reads prunable gitdir file points to non-existent location. Recheck a disposable real git repository mounted into an owned conversation container, comparing host and container git worktree list/status while it is alive; a local worktree does not test the filesystem boundary. Blocked without Docker.
Exact CLI recipe & bug checks
If docker info cannot reach the daemon but dockerd is installed, start it (setsid dockerd); without one, record both Docker rows blocked naming the missing daemon. With a daemon, run the image with -p 127.0.0.1:8000:8000, open http://localhost:8000/canvas and expect the Add a backend step (testid=onboarding-step-check-backend, Next disabled until a key is typed); onboarding-skip leads to testid=api-key-entry-screen. With -e AGENT_CANVAS_ALLOW_LAN_SESSION_KEY=true the key is injected and first run opens on Choose your agent, under the telemetry consent dialog. npm run build:docker behind a TLS-intercepting proxy needs the proxy CA inside the build stages (without it uv pip install / pip install in the final stage fail with CERTIFICATE_VERIFY_FAILED: self-signed certificate in certificate chain), and Docker Hub may answer 429. Build a scratch copy of the Dockerfile that only adds the CA, never an edited docker/Dockerfile: after FROM node:24-slim AS frontend-build add COPY --from=ccrca ca-bundle.crt /tmp/qa-ca.crt and ENV NODE_EXTRA_CA_CERTS=/tmp/qa-ca.crt npm_config_cafile=/tmp/qa-ca.crt, after the first USER root add the same COPY and ENV SSL_CERT_FILE=/tmp/qa-ca.crt PIP_CERT=/tmp/qa-ca.crt REQUESTS_CA_BUNDLE=/tmp/qa-ca.crt UV_NATIVE_TLS=1, then docker build -f <copy> --build-context ccrca=<dir holding ca-bundle.crt> --network host --build-arg AGENT_SERVER_IMAGE=<images.agentServer>:<versions.agentServer>-python --build-arg AGENT_SERVER_VERSION=<versions.agentServer> --build-arg AUTOMATION_VERSION=<versions.automation> --build-arg VITE_BASE_PATH=/canvas -t qa-agent-canvas:target . (the pins scripts/docker-build.mjs reads from config/defaults.json; about 5 min on a warm cache). Publish it on a free port (-p 127.0.0.1:<free>:8000; 8000 and the 188xx blocks may belong to other runs). Docker Hub may answer 429 Too Many Requests for node:24-slim; in the scratch copy only, replace both FROM node:24-slim lines with FROM mirror.gcr.io/library/node:24-slim (arrange; say so in the note). Run the unmodified image with docker run -d --name <qa-name> -p 127.0.0.1:<free>:8000 qa-agent-canvas:target. On a kernel without IPv6, docker logs <qa-name> shows IPv6 is not available; binding the frontend to 0.0.0.0. and Static-server + proxy listening on http://0.0.0.0:8000/canvas/, /canvas/ answers 200 within about 25 s and docker inspect -f '{{.State.Status}}' <qa-name> is still running minutes later (#18178 fixed by #18180, verified 2026-10-13); docker exec <qa-name> ls /opt/agent-canvas lists the mars-*.mjs bridge modules (#18343). The container's generated key is docker exec <qa-name> cat /home/openhands/.openhands/agent-canvas/api-key.txt (write it to $OH_VERIFY_RUN/private/, fill with --value-file). Settings → Application (/canvas/settings/app) shows the image's Version (agent-canvas-update-card), which matches jq -r .version package.json. Remove the containers and the image afterwards (docker rm -f, docker rmi). Record F26.lib-host-app blocked naming the missing host-app example. command -v helm kubectl prints nothing here: record F26.helm-chart blocked naming the missing helm binary and cluster; with them, helm lint helm/agent-canvas and helm template qa helm/agent-canvas render the StatefulSet, Service and Ingress, and helm install follows helm/agent-canvas/README.md.
Desktop splash and windowF26.desktop-boot-splash · F26.desktop-main-windowKnown issue
Scenario F26--desktop-splash-and-window · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoStart a private display and the app in one process group:
Q=$OH_VERIFY_RUN/private/f26-desktop; mkdir -p $Q/shots; setsid nohup sh -c "echo \$\$ > $Q/pgid; Xvfb :78 -screen 0 1440x1000x24 -nolisten tcp & sleep 1; DISPLAY=:78 HOME=$Q/home OH_CANVAS_SAFE_STATE_DIR=$Q/state LOCAL_BACKEND_API_KEY=qa-f26-desktop-key OH_SECRET_KEY=qa-f26-secret DO_NOT_TRACK=1 exec node_modules/.bin/electron --no-sandbox electron" > $Q/desktop.log 2>&1 &, then capture frames:for i in $(seq 1 40); do sleep 1; DISPLAY=:78 import -window root $Q/shots/s$(printf %02d $i).png; done. Read the frames: the first seconds show the splash (logo,OpenHands Agent Canvas,AI coding agent interface, spinner,Starting backend services…, then lines such asagent-server: {"asctime": ..., the noteFirst launch downloads Python + the OpenHands agent server.and a Show details button); a later frame shows the main window with aFile Edit View Windowmenu and the first-run Choose your agent step under the consent dialog namingthe local backend "Local" at http://localhost:8000(about 30 s on a warm cache, minutes on the first download). While it runs,curl -s http://127.0.0.1:8000/mars/health | head -c 15prints<!DOCTYPE html>: the desktop stack's ingress hosts no bridge (marsWebBridge: false,F26.mars-web-bridge). Copy the frames you cite into$OH_VERIFY_RUN/evidence/F26.desktop-boot-splash/and.../F26.desktop-main-window/. Stop in two steps:kill -TERM $(cat $Q/pgid)signals only the Electron launcher (it quits and stops its backend services); after about 10 s ports 8000, 18000, 18001 and 3001 are free again. Thenkill -TERM -- -$(cat $Q/pgid)ends Xvfb;ps -o pid,cmd -g $(cat $Q/pgid)lists nothing.
Expect
the Electron app first shows a dark splash (logo, OpenHands Agent Canvas, spinner, Starting backend services…, then live service-log lines, a first-launch note and Show details).
after boot a native window with a File/Edit/View/Window menu loads http://localhost:8000 with the key injected (first run, no API-key screen).
Linked known issues
Known issue #17112 (F26.desktop-main-window F26.desktop-external-links): Desktop main-window navigation, an OAuth popup redirect, or a nested popup can render a remote page inside the app frame. Recheck all three on an owned running Electron shell with a harmless remote page and loopback redirect fixture; the browser harness cannot operate Electron windows and the installed Electron binary is unavailable here.
Known issue #16774 (F26.desktop-boot-splash F26.desktop-main-window): The macOS 1.14.0 DMG installs an app that Gatekeeper reports as damaged instead of booting. Recheck the actual downloaded signed/notarized release artifact through install and first launch on macOS; starting source Electron or removing quarantine would bypass the reported failure. That release DMG/signature fixture is unavailable in this run.
Exact CLI recipe & bug checks
Start a private display and the app in one process group: Q=$OH_VERIFY_RUN/private/f26-desktop; mkdir -p $Q/shots; setsid nohup sh -c "echo \$\$ > $Q/pgid; Xvfb :78 -screen 0 1440x1000x24 -nolisten tcp & sleep 1; DISPLAY=:78 HOME=$Q/home OH_CANVAS_SAFE_STATE_DIR=$Q/state LOCAL_BACKEND_API_KEY=qa-f26-desktop-key OH_SECRET_KEY=qa-f26-secret DO_NOT_TRACK=1 exec node_modules/.bin/electron --no-sandbox electron" > $Q/desktop.log 2>&1 &, then capture frames: for i in $(seq 1 40); do sleep 1; DISPLAY=:78 import -window root $Q/shots/s$(printf %02d $i).png; done. Read the frames: the first seconds show the splash (logo, OpenHands Agent Canvas, AI coding agent interface, spinner, Starting backend services…, then lines such as agent-server: {"asctime": ..., the note First launch downloads Python + the OpenHands agent server. and a Show details button); a later frame shows the main window with a File Edit View Window menu and the first-run Choose your agent step under the consent dialog naming the local backend "Local" at http://localhost:8000 (about 30 s on a warm cache, minutes on the first download). While it runs, curl -s http://127.0.0.1:8000/mars/health | head -c 15 prints <!DOCTYPE html>: the desktop stack's ingress hosts no bridge (marsWebBridge: false, F26.mars-web-bridge). Copy the frames you cite into $OH_VERIFY_RUN/evidence/F26.desktop-boot-splash/ and .../F26.desktop-main-window/. Stop in two steps: kill -TERM $(cat $Q/pgid) signals only the Electron launcher (it quits and stops its backend services); after about 10 s ports 8000, 18000, 18001 and 3001 are free again. Then kill -TERM -- -$(cat $Q/pgid) ends Xvfb; ps -o pid,cmd -g $(cat $Q/pgid) lists nothing.
External linksF26.desktop-external-linksKnown issueNot run
Scenario F26--external-links · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoNot driven: record
not-run(harness gap: no verb clicks inside the Electron window or observes the OS browser handoff).
Expect
external links in the desktop window open in the system browser for allowed schemes only. Not driven: the harness cannot click inside the Electron window.
Linked known issues
Known issue #17112 (F26.desktop-main-window F26.desktop-external-links): Desktop main-window navigation, an OAuth popup redirect, or a nested popup can render a remote page inside the app frame. Recheck all three on an owned running Electron shell with a harmless remote page and loopback redirect fixture; the browser harness cannot operate Electron windows and the installed Electron binary is unavailable here.
Known issue #16872 (F26.desktop-external-links): Non-browser URI schemes were reported to reach the OS handler from the desktop popup flow. Current source now parses hosts and restricts external protocols; recheck refusal in a running owned Electron shell before concluding resolution. Observe an inert denied test scheme without invoking any real OS handler. The browser harness cannot drive Electron and no installed Electron binary is available here.
Exact CLI recipe & bug checks
Not driven: record not-run (harness gap: no verb clicks inside the Electron window or observes the OS browser handoff).
KV survives authentication rotationF26.session-key-rotated · F26.session-key-persistKnown failure
Scenario F26--kv-survives-authentication-rotation · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoWith no explicit
AUTOMATION_KV_SECRET, create an owned inert automation and PUT a harmless marker to/api/automation/v1/kv/qa-session-rotation?automation_id=<id>throughapi --write; GET must return that marker. Runrestart --rotate-keyanddoctor, then GET again using the helper's fresh private key. Expected: rotating authentication preserves encrypted automation state. Known failure, reproduced 2026-10-12 against automation 1.19.3: the pre-rotation read is 200 and the fresh-auth read is 500Failed to decrypt value(#18301). This uses only disposable state and does not establish the original incident's data-loss history.
Expect
in local mode a stale session key left in the browser (the launcher's key changed since the last visit) is replaced by the injected key on load: the app opens on Home without the API-key screen, and the stored Local backend holds the new key (public mode asks for it instead: F01's api-key-entry row).
in local mode without LOCAL_BACKEND_API_KEY the launcher generates a session key, saves it under $HOME/.openhands/agent-canvas/api-key.txt and injects the same key again on the next start.
Known bug observations
Known failure, reproduced 2026-10-12 against automation 1.19.3: the pre-rotation read is 200 and the fresh-auth read is 500 Failed to decrypt value (#18301). This uses only disposable state and does not establish the original incident's data-loss history.
Exact CLI recipe & bug checks
With no explicit AUTOMATION_KV_SECRET, create an owned inert automation and PUT a harmless marker to /api/automation/v1/kv/qa-session-rotation?automation_id=<id> through api --write; GET must return that marker. Run restart --rotate-key and doctor, then GET again using the helper's fresh private key. Expected: rotating authentication preserves encrypted automation state. Known failure, reproduced 2026-10-12 against automation 1.19.3: the pre-rotation read is 200 and the fresh-auth read is 500 Failed to decrypt value (#18301). This uses only disposable state and does not establish the original incident's data-loss history.
Partial SQLite migration startupF26.runtime-servicesKnown failureKnown issue
Scenario F26--partial-sqlite-migration-startup · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoStop only the owned automation service, make a cold backup of
<run>/private/automation/automations.db, and confirm itsautomation_git_sync_org_configalready hasconsecutive_auth_failures. In that disposable DB only, setalembic_version.version_numto028; restart with a bounded timeout, then open/automations. Expected: required-service startup fails explicitly or the partial schema recovers without corrupting state. Known failure, reproduced 2026-10-12 against automation 1.19.3: migration 029 reports the duplicate column and exits 3, the launcher remains alive, and Canvas shows Automations Unavailable (#18299). Restore the cold backup, restart and doctor before continuing. Never apply this fixture to an operator database.
Expect
/server_info.runtime_services lists the agent-server, ingress, frontend and automation URLs as the agent sees them, and a new conversation's system prompt carries a matching <RUNTIME_SERVICES> block.
Known bug observations
Known failure, reproduced 2026-10-12 against automation 1.19.3: migration 029 reports the duplicate column and exits 3, the launcher remains alive, and Canvas shows Automations Unavailable (#18299). Restore the cold backup, restart and doctor before continuing. Never apply this fixture to an operator database.
Linked known issues
Known issue #16720 (F26.runtime-services): CPU-heavy commands in several conversations can starve Agent Server and stop the UI loading; the report requests lower priority for agent commands than the service. Recheck with a disposable bounded workload, recorded CPU/resource limits and real command observations, never arbitrary stress on the shared machine. The exact Rust workload and affected resource envelope are missing, and real agent execution needs a model key. The backend counterpart is OpenHands/software-agent-sdk#4575.
Known issue #16690 (F26.docker-image F26.runtime-services): A Docker deployment with nine chats becomes slow/disconnected, then reloads lose the list and startup proxies report ECONNREFUSED. Recheck on the affected Windows-host Docker configuration with retained conversation state and a controlled workload; creating a few empty local chats cannot prove the reported resource/runtime failure. This run lacks Docker and the original conversation/workload fixture.
Known issue #16300 (F26.runtime-services): The npm launcher can fail while building LiteLLM native dependencies when resolved AWS crates require a newer Rust compiler, leaving backend services unavailable. Recheck an isolated uncached uv environment with litellm==1.95.0, the reported Rust 1.93.1/toolchain/platform and dependency resolution; a warm current backend cache cannot exercise that install failure. The historical dependency/toolchain fixture is unavailable.
Known issue #16208 (F26.runtime-services): On Fedora Rawhide/Python 3.14.6, settings work but starting any conversation returns HTTP 500. Recheck a native Fedora environment with the reported SDK/runtime and model/profile, including the underlying server traceback; this macOS current-runtime stack cannot prove that platform-specific failure.
Known issue OpenHands/software-agent-sdk#4901 (F26.runtime-services): closing an ACP conversation on POSIX is reported to leave the adapter's descendant Node/CLI processes alive after the top npm process exits. Recheck an owned authenticated ACP provider command chain, record its exact descendant identities before close and inspect only those identities afterwards; cleanup must target those owned groups. The current run has no authorized authenticated ACP runtime for this path.
Known issue OpenHands/software-agent-sdk#5974 (F26.runtime-services; workspace picker in F03): a saved-workspace mutation waiting for its registry file lock can block the Agent Server event loop, including /alive, until the lock is released. Recheck only an owned registry using a bounded lock holder, an independently confirmed waiting mutation, concurrent liveness probes and a successful post-release readback; the original evidence uses Linux kernel lock-wait readback. A healthy idle server or concurrent writes without deliberate lock contention do not cover it.
Exact CLI recipe & bug checks
Stop only the owned automation service, make a cold backup of <run>/private/automation/automations.db, and confirm its automation_git_sync_org_config already has consecutive_auth_failures. In that disposable DB only, set alembic_version.version_num to 028; restart with a bounded timeout, then open /automations. Expected: required-service startup fails explicitly or the partial schema recovers without corrupting state. Known failure, reproduced 2026-10-12 against automation 1.19.3: migration 029 reports the duplicate column and exits 3, the launcher remains alive, and Canvas shows Automations Unavailable (#18299). Restore the cold backup, restart and doctor before continuing. Never apply this fixture to an operator database.
Occupied internal frontend portF26.cli-port-in-useKnown failure
Scenario F26--occupied-internal-frontend-port · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoReserve a loopback frontend port in an owned listener and run the genuine launcher with separate free ingress/backend/automation ports. The reported default-port branch can also be observed read-only when port 3001 is already occupied; do not stop its owner. Expected: an occupied internal frontend port receives a free fallback while explicit service-port conflicts keep their existing semantics. Known failure, reproduced 2026-10-12:
agent-canvas --port 18940exits 1 withfrontend: port 3001, despite the ingress override (#17546, #16226). An explicitOH_CANVAS_SAFE_VITE_PORTis a current workaround; it does not provide automatic fallback.
Expect
an occupied ingress (or service) port stops the launch with Cannot start: the following ports are already in use naming each busy port.
Known bug observations
Known failure, reproduced 2026-10-12: agent-canvas --port 18940 exits 1 with frontend: port 3001, despite the ingress override (#17546, #16226). An explicit OH_CANVAS_SAFE_VITE_PORT is a current workaround; it does not provide automatic fallback.
Exact CLI recipe & bug checks
Reserve a loopback frontend port in an owned listener and run the genuine launcher with separate free ingress/backend/automation ports. The reported default-port branch can also be observed read-only when port 3001 is already occupied; do not stop its owner. Expected: an occupied internal frontend port receives a free fallback while explicit service-port conflicts keep their existing semantics. Known failure, reproduced 2026-10-12: agent-canvas --port 18940 exits 1 with frontend: port 3001, despite the ingress override (#17546, #16226). An explicit OH_CANVAS_SAFE_VITE_PORT is a current workaround; it does not provide automatic fallback.
Required frontend deathF26.runtime-servicesKnown failureKnown issue
Scenario F26--required-frontend-death · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoAfter a healthy private launch, identify only its required static-server PID using launcher ancestry plus its owned frontend port, then send that child SIGKILL. Open
/and check the owned launcher's liveness and authenticated/api/settings. Expected: an unexpected required-service death stops the stack and reports failure. Known failure, reproduced 2026-10-12: the launcher and backend remain alive, settings returns 200, and the main UI returns 502 Bad Gateway (#16927). This live trigger is SIGKILL, not a port-race EADDRINUSE; the failing required-child exit branch is shared. Restart and doctor to restore the run.
Expect
/server_info.runtime_services lists the agent-server, ingress, frontend and automation URLs as the agent sees them, and a new conversation's system prompt carries a matching <RUNTIME_SERVICES> block.
Known bug observations
Known failure, reproduced 2026-10-12: the launcher and backend remain alive, settings returns 200, and the main UI returns 502 Bad Gateway (#16927). This live trigger is SIGKILL, not a port-race EADDRINUSE; the failing required-child exit branch is shared. Restart and doctor to restore the run.
Linked known issues
Known issue #16720 (F26.runtime-services): CPU-heavy commands in several conversations can starve Agent Server and stop the UI loading; the report requests lower priority for agent commands than the service. Recheck with a disposable bounded workload, recorded CPU/resource limits and real command observations, never arbitrary stress on the shared machine. The exact Rust workload and affected resource envelope are missing, and real agent execution needs a model key. The backend counterpart is OpenHands/software-agent-sdk#4575.
Known issue #16690 (F26.docker-image F26.runtime-services): A Docker deployment with nine chats becomes slow/disconnected, then reloads lose the list and startup proxies report ECONNREFUSED. Recheck on the affected Windows-host Docker configuration with retained conversation state and a controlled workload; creating a few empty local chats cannot prove the reported resource/runtime failure. This run lacks Docker and the original conversation/workload fixture.
Known issue #16300 (F26.runtime-services): The npm launcher can fail while building LiteLLM native dependencies when resolved AWS crates require a newer Rust compiler, leaving backend services unavailable. Recheck an isolated uncached uv environment with litellm==1.95.0, the reported Rust 1.93.1/toolchain/platform and dependency resolution; a warm current backend cache cannot exercise that install failure. The historical dependency/toolchain fixture is unavailable.
Known issue #16208 (F26.runtime-services): On Fedora Rawhide/Python 3.14.6, settings work but starting any conversation returns HTTP 500. Recheck a native Fedora environment with the reported SDK/runtime and model/profile, including the underlying server traceback; this macOS current-runtime stack cannot prove that platform-specific failure.
Known issue OpenHands/software-agent-sdk#4901 (F26.runtime-services): closing an ACP conversation on POSIX is reported to leave the adapter's descendant Node/CLI processes alive after the top npm process exits. Recheck an owned authenticated ACP provider command chain, record its exact descendant identities before close and inspect only those identities afterwards; cleanup must target those owned groups. The current run has no authorized authenticated ACP runtime for this path.
Known issue OpenHands/software-agent-sdk#5974 (F26.runtime-services; workspace picker in F03): a saved-workspace mutation waiting for its registry file lock can block the Agent Server event loop, including /alive, until the lock is released. Recheck only an owned registry using a bounded lock holder, an independently confirmed waiting mutation, concurrent liveness probes and a successful post-release readback; the original evidence uses Linux kernel lock-wait readback. A healthy idle server or concurrent writes without deliberate lock contention do not cover it.
Exact CLI recipe & bug checks
After a healthy private launch, identify only its required static-server PID using launcher ancestry plus its owned frontend port, then send that child SIGKILL. Open / and check the owned launcher's liveness and authenticated /api/settings. Expected: an unexpected required-service death stops the stack and reports failure. Known failure, reproduced 2026-10-12: the launcher and backend remain alive, settings returns 200, and the main UI returns 502 Bad Gateway (#16927). This live trigger is SIGKILL, not a port-race EADDRINUSE; the failing required-child exit branch is shared. Restart and doctor to restore the run.
Static-server credential argvF26.runtime-servicesKnown failureKnown issue
Scenario F26--static-server-credential-argv · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoInspect only this run's owned static-server process for the frontend port. Read the private key in memory and retain booleans indicating whether
--session-api-keyis present and whether that literal key occurs in argv; never print argv or the key. Expected: other host users cannot recover the session key from command-line arguments. Known failure, reproduced 2026-10-12: both booleans are true for the owned static frontend (#16383). Docker remains a separate unavailable prerequisite here.
Expect
/server_info.runtime_services lists the agent-server, ingress, frontend and automation URLs as the agent sees them, and a new conversation's system prompt carries a matching <RUNTIME_SERVICES> block.
Known bug observations
Known failure, reproduced 2026-10-12: both booleans are true for the owned static frontend (#16383). Docker remains a separate unavailable prerequisite here.
Linked known issues
Known issue #16720 (F26.runtime-services): CPU-heavy commands in several conversations can starve Agent Server and stop the UI loading; the report requests lower priority for agent commands than the service. Recheck with a disposable bounded workload, recorded CPU/resource limits and real command observations, never arbitrary stress on the shared machine. The exact Rust workload and affected resource envelope are missing, and real agent execution needs a model key. The backend counterpart is OpenHands/software-agent-sdk#4575.
Known issue #16690 (F26.docker-image F26.runtime-services): A Docker deployment with nine chats becomes slow/disconnected, then reloads lose the list and startup proxies report ECONNREFUSED. Recheck on the affected Windows-host Docker configuration with retained conversation state and a controlled workload; creating a few empty local chats cannot prove the reported resource/runtime failure. This run lacks Docker and the original conversation/workload fixture.
Known issue #16300 (F26.runtime-services): The npm launcher can fail while building LiteLLM native dependencies when resolved AWS crates require a newer Rust compiler, leaving backend services unavailable. Recheck an isolated uncached uv environment with litellm==1.95.0, the reported Rust 1.93.1/toolchain/platform and dependency resolution; a warm current backend cache cannot exercise that install failure. The historical dependency/toolchain fixture is unavailable.
Known issue #16208 (F26.runtime-services): On Fedora Rawhide/Python 3.14.6, settings work but starting any conversation returns HTTP 500. Recheck a native Fedora environment with the reported SDK/runtime and model/profile, including the underlying server traceback; this macOS current-runtime stack cannot prove that platform-specific failure.
Known issue OpenHands/software-agent-sdk#4901 (F26.runtime-services): closing an ACP conversation on POSIX is reported to leave the adapter's descendant Node/CLI processes alive after the top npm process exits. Recheck an owned authenticated ACP provider command chain, record its exact descendant identities before close and inspect only those identities afterwards; cleanup must target those owned groups. The current run has no authorized authenticated ACP runtime for this path.
Known issue OpenHands/software-agent-sdk#5974 (F26.runtime-services; workspace picker in F03): a saved-workspace mutation waiting for its registry file lock can block the Agent Server event loop, including /alive, until the lock is released. Recheck only an owned registry using a bounded lock holder, an independently confirmed waiting mutation, concurrent liveness probes and a successful post-release readback; the original evidence uses Linux kernel lock-wait readback. A healthy idle server or concurrent writes without deliberate lock contention do not cover it.
Exact CLI recipe & bug checks
Inspect only this run's owned static-server process for the frontend port. Read the private key in memory and retain booleans indicating whether --session-api-key is present and whether that literal key occurs in argv; never print argv or the key. Expected: other host users cannot recover the session key from command-line arguments. Known failure, reproduced 2026-10-12: both booleans are true for the owned static frontend (#16383). Docker remains a separate unavailable prerequisite here.
Keep-alive proxy timeout listenersF26.runtime-servicesKnown failureKnown issue
Scenario F26--keep-alive-proxy-timeout-listeners · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoThrough the owned ingress, send 20 sequential authenticated
/api/conversations/search?limit=1requests on one HTTP keep-alive connection, recording response statuses and the reused local socket port, then inspect only the owned ingress's warning line. Expected: one socket does not acquire one timeout listener per request. Known failure, reproduced 2026-10-12: all 20 requests are 200 on the same socket and ingress emitsMaxListenersExceededWarningfor 11 timeout listeners (#16839)./server_infouses a custom enrichment handler here, so it is not the generic proxy branch. Keep header values and full logs private.
Expect
/server_info.runtime_services lists the agent-server, ingress, frontend and automation URLs as the agent sees them, and a new conversation's system prompt carries a matching <RUNTIME_SERVICES> block.
Known bug observations
Known failure, reproduced 2026-10-12: all 20 requests are 200 on the same socket and ingress emits MaxListenersExceededWarning for 11 timeout listeners (#16839). /server_info uses a custom enrichment handler here, so it is not the generic proxy branch. Keep header values and full logs private.
Linked known issues
Known issue #16720 (F26.runtime-services): CPU-heavy commands in several conversations can starve Agent Server and stop the UI loading; the report requests lower priority for agent commands than the service. Recheck with a disposable bounded workload, recorded CPU/resource limits and real command observations, never arbitrary stress on the shared machine. The exact Rust workload and affected resource envelope are missing, and real agent execution needs a model key. The backend counterpart is OpenHands/software-agent-sdk#4575.
Known issue #16690 (F26.docker-image F26.runtime-services): A Docker deployment with nine chats becomes slow/disconnected, then reloads lose the list and startup proxies report ECONNREFUSED. Recheck on the affected Windows-host Docker configuration with retained conversation state and a controlled workload; creating a few empty local chats cannot prove the reported resource/runtime failure. This run lacks Docker and the original conversation/workload fixture.
Known issue #16300 (F26.runtime-services): The npm launcher can fail while building LiteLLM native dependencies when resolved AWS crates require a newer Rust compiler, leaving backend services unavailable. Recheck an isolated uncached uv environment with litellm==1.95.0, the reported Rust 1.93.1/toolchain/platform and dependency resolution; a warm current backend cache cannot exercise that install failure. The historical dependency/toolchain fixture is unavailable.
Known issue #16208 (F26.runtime-services): On Fedora Rawhide/Python 3.14.6, settings work but starting any conversation returns HTTP 500. Recheck a native Fedora environment with the reported SDK/runtime and model/profile, including the underlying server traceback; this macOS current-runtime stack cannot prove that platform-specific failure.
Known issue OpenHands/software-agent-sdk#4901 (F26.runtime-services): closing an ACP conversation on POSIX is reported to leave the adapter's descendant Node/CLI processes alive after the top npm process exits. Recheck an owned authenticated ACP provider command chain, record its exact descendant identities before close and inspect only those identities afterwards; cleanup must target those owned groups. The current run has no authorized authenticated ACP runtime for this path.
Known issue OpenHands/software-agent-sdk#5974 (F26.runtime-services; workspace picker in F03): a saved-workspace mutation waiting for its registry file lock can block the Agent Server event loop, including /alive, until the lock is released. Recheck only an owned registry using a bounded lock holder, an independently confirmed waiting mutation, concurrent liveness probes and a successful post-release readback; the original evidence uses Linux kernel lock-wait readback. A healthy idle server or concurrent writes without deliberate lock contention do not cover it.
Exact CLI recipe & bug checks
Through the owned ingress, send 20 sequential authenticated /api/conversations/search?limit=1 requests on one HTTP keep-alive connection, recording response statuses and the reused local socket port, then inspect only the owned ingress's warning line. Expected: one socket does not acquire one timeout listener per request. Known failure, reproduced 2026-10-12: all 20 requests are 200 on the same socket and ingress emits MaxListenersExceededWarning for 11 timeout listeners (#16839). /server_info uses a custom enrichment handler here, so it is not the generic proxy branch. Keep header values and full logs private.
Default Rich logging responsivenessF26.remote-backendKnown failureKnown issue
Scenario F26--default-rich-logging-responsiveness · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do — reading outline
- DoTemporarily replace only an owned Agent Server with its same pinned interpreter/state on the same port; unset
LOG_JSON,CI,GITHUB_ACTIONSandLOG_RICH_TRACEBACKS, and retain its PID for cleanup. Create a bare-tool conversation whose dummy LLM points at closedhttp://127.0.0.1:9/v1with zero retries. Start 60 direct/aliveprobes concurrently with a genuine Canvas Send message; inspect the rendered connection error and final conversation status. Expected: every probe stays below 1.5 s during failure. Known failure, reproduced 2026-10-12 on Agent Server 1.54.0: the conversation endserrorand one health probe stalls 2.091 s (OpenHands/software-agent-sdk#5633). This is a finite connection-error case, separate from the unprovided cyclic websocket fixture in SDK #5373. Stop that owned replacement, restore the normal launcher and doctor.
Expect
from a normal Canvas, backend selector → Add Backend → Agent-server → Remote shows the self-hosting guidance and connects to another instance (stand-in for a self-hosted VM, docs/SELF_HOSTING.md).
Known bug observations
Known failure, reproduced 2026-10-12 on Agent Server 1.54.0: the conversation ends error and one health probe stalls 2.091 s (OpenHands/software-agent-sdk#5633). This is a finite connection-error case, separate from the unprovided cyclic websocket fixture in SDK #5373. Stop that owned replacement, restore the normal launcher and doctor.
Linked known issues
Known issue #15434 (F26.remote-backend): The embedded VS Code action on a self-hosted reverse-proxy origin was reported to reopen Canvas because its advertised URL omitted the separate editor port/path. Current launchers include an ingress /vscode/ route; recheck against the affected HTTPS proxy deployment with an actually available editor binary and emitted URL before concluding resolution. This run lacks that self-hosted proxy/editor deployment.
Known issue OpenHands/software-agent-sdk#5373 (F26.remote-backend): A self-hosted Agent Server using default Rich traceback logging can wedge its event loop and grow memory when a websocket error has a cyclic/deep exception chain; Canvas then times out or disconnects. The npm launcher forces JSON logging, so rechecking needs a separately hosted default-Rich backend plus the reported pathological peer/backlog reproducer and bounded memory/time isolation. That original peer/driver is not included in the issue body; do not synthesize a server patch or stress the shared host and claim a live UI reproduction.
Exact CLI recipe & bug checks
Temporarily replace only an owned Agent Server with its same pinned interpreter/state on the same port; unset LOG_JSON, CI, GITHUB_ACTIONS and LOG_RICH_TRACEBACKS, and retain its PID for cleanup. Create a bare-tool conversation whose dummy LLM points at closed http://127.0.0.1:9/v1 with zero retries. Start 60 direct /alive probes concurrently with a genuine Canvas Send message; inspect the rendered connection error and final conversation status. Expected: every probe stays below 1.5 s during failure. Known failure, reproduced 2026-10-12 on Agent Server 1.54.0: the conversation ends error and one health probe stalls 2.091 s (OpenHands/software-agent-sdk#5633). This is a finite connection-error case, separate from the unprovided cyclic websocket fixture in SDK #5373. Stop that owned replacement, restore the normal launcher and doctor.
Desktop macos titlebarF26.desktop-macos-titlebarBlocked
Scenario F26--contract-f26-desktop-macos-titlebar · Permalink · Source
Given
Use F26’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.
Do
This behavior has no separately named recipe in this snapshot. Use the family’s entry points and the contract below; the page does not invent a replay.
Expect
on macOS the desktop window hides the native title bar and reserves a 28 px drag band (titlebar-drag-region, aria-hidden) above the shell, so the sidebar logo clears the traffic lights and the window can be dragged; the band is dropped in native fullscreen, also after a reload while fullscreen. Linux and Windows windows and browser tabs render no band (window.desktopShell is undefined in a tab; platform is linux on Linux). The band is rendered by the root layout, its error shell and the config-loading spinner, not by the first-run onboarding screen. Blocked without macOS (#17474).
Family notes, prerequisites for known issues & limits Known failureKnown issueBlocked
control-openhands launchcovers only the full stack and--public. Partial stacks and--hostneed a hand-started launcher with its ownHOME,OH_CANVAS_SAFE_STATE_DIRand all threeOH_CANVAS_SAFE_*_PORTvariables; without them it writes into the real~/.openhands/agent-canvasand grabs the default ports 8000/18000/18001/3001 that another agent may be using.- A hand-started launcher's first start downloads the agent-server into its private
HOMEcache (30–90 s). Prefix assignments are expanded left to right, soHOME=$Q/home UV_CACHE_DIR=$HOME/.cache/uvpoints the cache into$Q/hometoo, not at your real cache. Reuse one$Qfor repeat launches to keep it warm. - The browser daemon refuses other origins unless you pass
--allow-externaltobrowser goto. Each port is its own origin, so a partial-stack page starts at first run again (onboarding and a consent dialog), independent of your run's onboarding. - In a frontend-only UI the Add a backend step's Next is enabled with an empty key, while on a LAN or
--publicstack it is disabled until a key is typed: the host the step probes differs (503 vs. auth-required). - A partial-stack origin keeps its stored backend in the browser profile across launches, so a second pass lands straight in the shell.
control-openhands browser resetbrings back the first run on every origin; thencontrol-openhands browser goto /andcontrol-openhands onboard --skiprestore your own run's baseline. - Which screen a frontend-only origin shows depends on the browser, not the launcher: a profile new to the origin gets Add a backend (first run owns the initial backend), while a profile that stores a backend for that origin (the
default-localentry a full launcher on the same port seeded, or any backend added there earlier) gets the recovery gate ofF25.recovery-gateas soon as that backend answers 503 (F26.frontend-only-returning). The no-toast check belongs to the first-run state only: on the gate,browser toasts --historylists genericAn error occurredstatus toasts raised by the failing 503 probes (two within 12 s here), with no hint of which request failed. Known failure (reproduced 2026-10-08): both toasts come from the free-models hydrator'sGET /api/llm/models/verified503, which nometa.disableToastguards on that screen (nor doesbackend-version.tsx); #18160. - Stopping a backend-only instance while the frontend-only page that uses it stays open is the same backend-down state as
F25.recovery-gateafter a reload; keep the returning-user bullet above as the frontend-only-specific check (it reproduces thedefault-localcase). - The launcher serves the bundled VS Code editor. #17660 made that opt-in (
OH_CANVAS_ENABLE_VSCODE=true) and #18048 reverted it, so only a checkout between the two needs the variable (launch --vscodesets it). A launcher that serves it reserves/vscode/: a frontend-only stack answers503there and logs[static] /vscode -> 503 (rejected); a stack with an agent-server lists/vscode → http://127.0.0.1:<agent-server port + 1000>in its ingress banner and hands the agent-server that editor port (19961 for the hand-started launchers here, 19000 for the desktop app), which the launcher's own port check does not cover. On a checkout between the two without the variable,/vscode/is an ordinary SPA URL (200). Here the agent-server has no editor binary (VSCode server binary not found, VSCode will be disabledin the launcher log,/api/vscode/status{"running":false,"enabled":true}), so the editor port stays free even then. - The port-in-use error is printed twice, the second time with a Node stack trace, and suggests
PORT=<other>rather than--port(#17949). The--publickey error's example saysnpm run dev -- --publicinstead of the npx command. - A backend-only instance's
/server_info.runtime_servicesstill describes the ingress as routing/* to the frontend, which it does not run (#17949). - In this sandbox
node_modules/electron/distis absent andnode node_modules/electron/install.jsfails withERR_ASSERTION(2026-10-13), so theF26.desktop-*rows stay blocked here. - The Electron app hardcodes ports 8000/18000/18001 (and the static server takes 3001), ignores
PORT, and refuses to start as root without--no-sandbox(Running as root without --no-sandbox is not supported).npm run desktopfirst runsbuild:app, which rebuildsbuild/under every other run: startnode_modules/.bin/electron --no-sandbox electrondirectly against the existing build. - Never stop the desktop app with one group-wide signal: when Xvfb dies with Electron, the app exits without stopping its backend, which is left orphaned (own process groups, parent 1) on 8000/18000/18001/3001 and blocks the next start. If that happens, find the orphans by their ports' command lines (
ps -eo pid,pgid,cmd, look for--port 18000,--port 18001,--port 3001,ingress.mjs --port 8000) and stop each group by id. - The generated session key lives in
$HOME/.openhands/agent-canvas/api-key.txt, not underOH_CANVAS_SAFE_STATE_DIR: a hand-started launcher without a privateHOMEreads and writes the operator's real key file. - A browser origin keeps the backends it stored on an earlier pass: after the LAN pass on 18960, a later loopback launch on 18960 shows the stale
QA_LANbackend (red, old key) instead of onboarding, although the page injects a valid key. Assert key-injection bullets through thewindowglobals, orbrowser resetfirst. - Electron's splash shows each raw service-log line (
agent-server: {"asctime": ...) as its status text during boot; the named phases only appear between them. build:libfrom a copy whosenode_modulesis a symlink emits bundled dependencies underdist/<absolute node_modules path>/...; that path is an artifact of the copy, not of the product.- **Bug candidate (
F26.host-bind-lan-optin):**scripts/bind-host.mjstells npm users to pass--allow-lan-session-key, but neitherbin/agent-canvas.mjsnorscripts/dev-with-automation.mjsparses or forwards it (onlyscripts/static-server.mjsand the Docker entrypoint do), and unknown flags are silently ignored (#17949, whose first item is exactly this; reproduced again on 2026-10-08 at d2c89252d). - Known issue #17946: the npm launcher never creates its
TMUX_TMPDIR, so hand-started instances share one tmux server and a restart of one resets the others' terminals. - Known issue #17763 (
F26.docker-image): The Docker entrypoint can leave protected Agent Server APIs unauthenticated when onlyLOCAL_BACKEND_API_KEYis supplied, because its export toOH_SESSION_API_KEYS_0is inside the generate-key branch. Recheck an owned container with a dummy custom key: unauthenticatedGET /api/conversations/searchmust be 401 and the same request with that dummy key 200./server_infois intentionally public and is not an auth probe. Blocked here without a Docker daemon. - Known issue #17623 (
F26.helm-chart): The chart defaults service-account-token automounting to true even with RBAC disabled, exposing an unused cluster credential to agent processes. Recheck a disposable Kubernetes installation using the default values and an explicit opt-in; inspect token existence/readability only, never its contents. Blocked without Helm and a Kubernetes cluster. - Known issue #17470 (
F26.docker-image): On Apple Silicon under Colima/Virtualization.framework, the image's Python services can exit with SIGILL while the frontend leaves onboarding at Disconnected. The report isolatesimport litellmand comparesOPENSSL_armcap=0; recheck on the affected ARM/Colima guest with the reported cryptography/OpenSSL combination. A native macOS npm stack is not that container environment. - Known issue #17112 (
F26.desktop-main-windowF26.desktop-external-links): Desktop main-window navigation, an OAuth popup redirect, or a nested popup can render a remote page inside the app frame. Recheck all three on an owned running Electron shell with a harmless remote page and loopback redirect fixture; the browser harness cannot operate Electron windows and the installed Electron binary is unavailable here. - Known issue #16872 (
F26.desktop-external-links): Non-browser URI schemes were reported to reach the OS handler from the desktop popup flow. Current source now parses hosts and restricts external protocols; recheck refusal in a running owned Electron shell before concluding resolution. Observe an inert denied test scheme without invoking any real OS handler. The browser harness cannot drive Electron and no installed Electron binary is available here. - Known issue #16774 (
F26.desktop-boot-splashF26.desktop-main-window): The macOS 1.14.0 DMG installs an app that Gatekeeper reports as damaged instead of booting. Recheck the actual downloaded signed/notarized release artifact through install and first launch on macOS; starting source Electron or removing quarantine would bypass the reported failure. That release DMG/signature fixture is unavailable in this run. - Known issue #16720 (
F26.runtime-services): CPU-heavy commands in several conversations can starve Agent Server and stop the UI loading; the report requests lower priority for agent commands than the service. Recheck with a disposable bounded workload, recorded CPU/resource limits and real command observations, never arbitrary stress on the shared machine. The exact Rust workload and affected resource envelope are missing, and real agent execution needs a model key. The backend counterpart is OpenHands/software-agent-sdk#4575. - Known issue #16690 (
F26.docker-imageF26.runtime-services): A Docker deployment with nine chats becomes slow/disconnected, then reloads lose the list and startup proxies report ECONNREFUSED. Recheck on the affected Windows-host Docker configuration with retained conversation state and a controlled workload; creating a few empty local chats cannot prove the reported resource/runtime failure. This run lacks Docker and the original conversation/workload fixture. - Known issue #16511 (
F26.docker-image): The documented Docker bind mount can be unwritable to container uid 10001, killing Agent Server/automation while the frontend still serves Bad Gateway. Recheck a disposable host-owned mode-700 mount and a writable control against an owned image; never chmod the operator's real state directory. Blocked here without Docker. - Known issue #16300 (
F26.runtime-services): The npm launcher can fail while building LiteLLM native dependencies when resolved AWS crates require a newer Rust compiler, leaving backend services unavailable. Recheck an isolated uncached uv environment withlitellm==1.95.0, the reported Rust 1.93.1/toolchain/platform and dependency resolution; a warm current backend cache cannot exercise that install failure. The historical dependency/toolchain fixture is unavailable. - Known issue #16208 (
F26.runtime-services): On Fedora Rawhide/Python 3.14.6, settings work but starting any conversation returns HTTP 500. Recheck a native Fedora environment with the reported SDK/runtime and model/profile, including the underlying server traceback; this macOS current-runtime stack cannot prove that platform-specific failure. - Known issue #15434 (
F26.remote-backend): The embedded VS Code action on a self-hosted reverse-proxy origin was reported to reopen Canvas because its advertised URL omitted the separate editor port/path. Current launchers include an ingress/vscode/route; recheck against the affected HTTPS proxy deployment with an actually available editor binary and emitted URL before concluding resolution. This run lacks that self-hosted proxy/editor deployment. - Known issue OpenHands/software-agent-sdk#5373 (
F26.remote-backend): A self-hosted Agent Server using default Rich traceback logging can wedge its event loop and grow memory when a websocket error has a cyclic/deep exception chain; Canvas then times out or disconnects. The npm launcher forces JSON logging, so rechecking needs a separately hosted default-Rich backend plus the reported pathological peer/backlog reproducer and bounded memory/time isolation. That original peer/driver is not included in the issue body; do not synthesize a server patch or stress the shared host and claim a live UI reproduction. - Known issue OpenHands/software-agent-sdk#5310 (
F26.docker-conversation-runtime): A Docker conversation can lose the auxiliary title profile at the runtime boundary, leaving an ACP conversation untitled or producing an unrelated title-auth error while the agent still works. Recheck a saved reachable title profile and an ACP conversation inside an owned per-conversation Docker runtime, comparing its title/profile metadata with the host; inspect only profile names and credential-presence booleans. Blocked without Docker and real auxiliary-title/ACP credentials. - Known issue OpenHands/software-agent-sdk#5307 (
F26.docker-conversation-runtime): New worktree on a Docker-backed conversation can create git metadata whose reverse path exists only inside the container, so the still-live host worktree readsprunable gitdir file points to non-existent location. Recheck a disposable real git repository mounted into an owned conversation container, comparing host and containergit worktree list/status while it is alive; a local worktree does not test the filesystem boundary. Blocked without Docker. - Known issue OpenHands/software-agent-sdk#4901 (
F26.runtime-services): closing an ACP conversation on POSIX is reported to leave the adapter's descendant Node/CLI processes alive after the top npm process exits. Recheck an owned authenticated ACP provider command chain, record its exact descendant identities before close and inspect only those identities afterwards; cleanup must target those owned groups. The current run has no authorized authenticated ACP runtime for this path. - Known issue OpenHands/software-agent-sdk#5974 (
F26.runtime-services; workspace picker in F03): a saved-workspace mutation waiting for its registry file lock can block the Agent Server event loop, including/alive, until the lock is released. Recheck only an owned registry using a bounded lock holder, an independently confirmed waiting mutation, concurrent liveness probes and a successful post-release readback; the original evidence uses Linux kernel lock-wait readback. A healthy idle server or concurrent writes without deliberate lock contention do not cover it. - Known issue OpenHands/software-agent-sdk#5731 (
F26.cli-public-needs-key): whitespace-only session keys remain configured credentials in the Agent Server. The Canvas launcher forwards a nonemptyLOCAL_BACKEND_API_KEYverbatim, so a whitespace-only deployment value can leave the public login unable to use its intended key. Recheck an owned loopback-only public deployment with a whitespace-only supplied key, its actual HTTP/WebSocket authentication and a normal nonblank control; never test the server's default broad bind on an exposed interface. The SDK's missing-OH_SECRET_KEYcipher fallback is a separate configuration branch from a Canvas launcher that supplies an encryption key.
Before you start
Use an OpenHands checkout at the source revision, with Node 24+, installed npm dependencies, uv / uvx, and a supported Chromium browser. The verification skill is the complete setup contract.
export PATH="$PWD/.agents/skills/verify-openhands/scripts:$PATH"
export OH_VERIFY_RUN=$(control-openhands launch --new --print-run)
control-openhands doctor
control-openhands onboard --skip
# Configure a model only for a scenario that needs one:
control-openhands llm preset deepseek
# When the run is complete:
control-openhands stop
F01 walks onboarding instead of skipping it. No-model checks run before model configuration. Keep OH_VERIFY_RUN set so commands address your own run. Desktop is 1440 × 1000; phone is 390 × 844. Model-backed checks need the configured provider key and budget.
Recipes run in source order within each family and can share fixtures. The page preserves that order; “as above” is a dependency on an earlier case. Setup commands and API writes arrange state. They do not prove a UI path. Follow the family cleanup steps.
A known failure preserves the intended expectation beside the observed defect. Executable known-bug blocks use xfail for a reproduced symptom and xpass when it stops reproducing. Cloud accounts, enterprise capabilities and other unavailable prerequisites remain explicit. “Not run” is different from “blocked”.
The maintained map describes application behavior at its pinned revision. Once a fix is verified in the application version under test, remove the obsolete failure note and keep the normal expectation. Dated evidence belongs in the backfill ledger.
Read the full map index and coverage boundaries, the CLI guide, or the earlier illustrated edition. The verification approach adapts Lauren Tan’s (@poteto) pstack verification-skill generators; attribution and adaptation.