Skip to content

web: Failed to send command over insecure HTTP origin (crypto.randomUUID is not a function) #55777

Description

@Sahado

Summary

Web UI fails to send any command when accessed via insecure HTTP LAN IP with crypto.randomUUID is not a function. Blocks all prompting from that origin.

Environment

  • opencode version: v2.0.29
  • OS: Linux 6.1.42-antix.1-amd64-smp x86_64 (akms-RV409-RV509-RV709)
  • Terminal: TERM=xterm-256color, COLORTERM=truecolor
  • Shell: /bin/bash
  • Install/channel: latest
  • Active plugins: opencode-browser (MCP browsermcp local enabled)

Reproduction

  1. Start opencode web/server and open it via LAN IP origin, e.g. http://10.243.146.72:49374/server/.../session/... (Chrome shows Not secure).
  2. Open session Browser assistance for online tasks.
  3. Type any prompt in Add follow-up, / for commands, @ for context... and submit.
  4. Toast appears immediately and nothing is sent.

Expected Behavior

Message is sent, or client generates an ID without requiring SecureContext (fallback to crypto.getRandomValues() / manual UUID).

Actual Behavior

Toast: Failed to send command - crypto.randomUUID is not a function. Session stays on Working with no request sent. 100% reproducible on that origin.

Additional Context

  • crypto.randomUUID() is only exposed in Secure Contexts (https://, http://localhost). http://10.243.146.72 is treated as insecure, so the call throws.
  • Workaround: accessing the same server via http://localhost:49374/... works.
  • Browser: Chromium-based with Browser MCP - Automate your browser... started debugging this browser banner visible (see screenshot).
  • Suggested fix: guard randomUUID usage in web client with fallback, e.g. crypto.randomUUID?.() ?? uuidv4(getRandomValues).

Activity

  1. opencode-agent commented on Oct 15, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report. I can reproduce this on v2.0.29, and it still happens on the latest dev (68510d9d6).

    What I did: ran opencode serve --hostname 0.0.0.0, opened the web UI in Chromium through the machine's LAN IP (where isSecureContext is false and crypto.randomUUID is undefined), opened a session and submitted /btw what is 2+2. The toast "Failed to send command – crypto.randomUUID is not a function" appears right away and nothing is sent. The screenshots below show it on v2.0.29 and on dev, and the script below reproduces it.

    The call that throws is in the /btw side-question command, which uses crypto.randomUUID() without checking that it exists. Because the error comes from a command, the toast says "command". I couldn't test a plain prompt here (no model credentials). If it also fails for you with ordinary text, not just a / command, please mention it. Until this is fixed, opening the UI via http://localhost (as you found) or over HTTPS avoids it.

    Related: #46168 and #48144 are the same insecure-origin crypto.randomUUID problem in other parts of the web UI (session delete, file attach).

    Screenshot

    Screenshot

    Reproduction script (repro.mjs)
    // Repro for #55777: web UI over a non-localhost HTTP origin (insecure context, no crypto.randomUUID).
    // 1. cd <project> && opencode serve --hostname 0.0.0.0 --port 49374   (note the printed server password)
    // 2. ORIGIN=http://<lan-ip>:49374 PASSWORD=<pw> DIR=<project> CHROME=<chromium> node repro.mjs
    // Needs playwright-core (`npm i playwright-core`) and a Chromium binary (`npx playwright-core install chromium`).
    // <lan-ip> must be a non-localhost address so the page is not a secure context (crypto.randomUUID is undefined).
    import { chromium } from "playwright-core"
    const { ORIGIN, PASSWORD, DIR } = process.env
    const auth = "Basic " + Buffer.from(`opencode:${PASSWORD}`).toString("base64")
    const res = await fetch(`${ORIGIN}/api/session`, {
      method: "POST",
      headers: { authorization: auth, "content-type": "application/json", "x-opencode-directory": DIR },
      body: JSON.stringify({ title: "repro 55777" }),
    }).then((r) => r.json())
    const id = res.data.id
    const browser = await chromium.launch({ executablePath: process.env.CHROME, args: ["--no-sandbox"] })
    const ctx = await browser.newContext({ httpCredentials: { username: "opencode", password: PASSWORD } })
    const page = await ctx.newPage()
    const errors = []
    page.on("pageerror", (e) => errors.push("pageerror: " + e.message))
    page.on("console", (m) => m.type() === "error" && errors.push("console: " + m.text()))
    await page.goto(ORIGIN, { waitUntil: "load" })
    console.log("isSecureContext:", await page.evaluate(() => isSecureContext), "typeof crypto.randomUUID:", await page.evaluate(() => typeof crypto.randomUUID))
    await page.waitForTimeout(3000)
    await page.getByText("repro 55777").first().click()
    await page.waitForTimeout(3000)
    const editor = page.locator('[contenteditable="true"]').first()
    await editor.click()
    await page.keyboard.type("/btw what is 2+2", { delay: 20 })
    await page.waitForTimeout(500)
    await page.keyboard.press("Escape") // close the slash popover if open
    await page.keyboard.press("Enter")
    await page.waitForTimeout(2500)
    await page.screenshot({ path: process.env.SHOT ?? "/workspace/repro/btw-insecure.png" })
    const body = await page.innerText("body")
    console.log("toast present:", /Failed to send command/.test(body))
    console.log(body.split("\n").filter((l) => /Failed|randomUUID/.test(l)).join("\n"))
    console.log(errors.join("\n"))
    await browser.close()

    Run it: bun repro.mjs

  2. drakeo338 commented on Oct 15, 2026

    @drakeo338

    I'm looking at this one and plan to open a fix shortly.

  3. drakeo338 commented on Oct 15, 2026

    @drakeo338

    Releasing this one; I couldn't land a fix I'm confident in, so it's free for anyone else to take.

  4. nosmile99 commented on Oct 15, 2026

    @nosmile99

    Verified independently on dev @ 775d1d0. Repro: serve the web UI over plain HTTP (LAN IP) so the context is insecure, submit any command → TypeError: crypto.randomUUID is not a function and the "Failed to send command" toast. Matches the bot's repro exactly.

    Root cause is NOT in packages/app/src/runtime/persistence/uuid.ts (that one already has an insecure-context fallback). Tracing the toast through composer/submit.ts → command.trigger → the /btw client command lands in packages/gui-extensions/src/btw/model.ts: ask() calls crypto.randomUUID() directly. In insecure contexts crypto exists but randomUUID is undefined, so it throws before anything else runs.

    I have a tested fix + regression test ready on a local branch:

    • const id = crypto.randomUUID?.() ?? Math.random().toString(16).slice(2) — mirrors the existing app-side fallback, no new strings.
    • New model.test.ts (2 tests): insecure-context test fails pre-fix with the exact TypeError, passes post-fix; secure-context control passes both ways. Post-fix: 2/2 green, typecheck (tsgo) clean, oxlint 0 errors.
    • One side note: model.ts statically imported @opencode/ui/toast (solid-sonner), which throws at import time in bun's test runtime and mock.module can't intercept the package specifier — so the toast UI is now lazy-imported in the empty-question branch only (per the repo's AGENTS.md dynamic-import pattern).

    Requesting assignment per the contribution protocol — happy to open the PR when assigned.

  5. Tarquinen commented on Nov 2, 2026

    @Tarquinen
    Member

    Thanks for the report! This was fixed by #60462, which shipped in v2.2.3. Please upgrade, and open a new issue if it still happens.

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions