Skip to content

Self-hosted video processing silently broken: .well-known/workflow/v1/* route handlers return the SPA HTML shell (Turbopack standalone build) #1944

Description

@RobinHoodO

Summary

On the self-hosted Docker image (ghcr.io/capsoftware/cap-web:latest), every recording gets stuck on "Processing failed". Uploads succeed (segments + manifest.json land in the bucket), but the recording never finishes processing.

Root cause: the Workflow DevKit control-plane routes under /.well-known/workflow/v1/* are not served by their route handlers — they return the Next.js app/SPA HTML shell with HTTP 200. Because the workflow engine advances every durable step by making an HTTP self-call to /.well-known/workflow/v1/flow (and /step), and that call gets a 200 text/html page back instead of the handler, the workflow silently never advances. No error is logged; the run stays pending forever.

This appears to be a Next.js 16 + Turbopack standalone build issue: apps/web/package.json builds with next build --turbopack, and the entire .well-known namespace is unreachable at runtime in the standalone server.

Environment

  • Image: ghcr.io/capsoftware/cap-web:latest (built 2026-06-13, Next 16.2.1, workflow@4.2.0-beta.73, @workflow/world-local@4.1.0-beta.46)
  • Self-hosted via the repo docker-compose.yml (MySQL + MinIO + media-server + cap-web)
  • Recording mode: desktopSegments (in-browser recorder)

Reproduction

  1. Self-host with the provided docker-compose.
  2. Record anything via the web recorder.
  3. Segments + manifest.json (is_complete: true) upload to the bucket; the media-server is never called; the cap is shown as "Processing failed".
  4. Inside the container, .workflow-data/runs/*.json shows the run stuck at "status": "pending" (only a run_created event), and video_uploads.phase stays processing with processing_error = NULL.

Evidence (the smoking gun)

Route handlers work everywhere except the .well-known namespace. Run inside the cap-web container:

// returns 200 text/html — the SPA shell, NOT the workflow handler
fetch("http://127.0.0.1:3000/.well-known/workflow/v1/flow",
  {method:"POST",headers:{"content-type":"application/json"},body:"{}"})
  .then(async r => console.log(r.status, r.headers.get("content-type")))
Request Result
POST /api/upload/signed 401 (handler runs ✅)
POST /api/auth/providers 400 (handler runs ✅)
POST /.well-known/workflow/v1/flow 200 text/html ❌
POST /.well-known/workflow/v1/step 200 text/html ❌
POST /.well-known/appspecific/anything (nonexistent) 200 text/html ❌

So the whole /.well-known/* namespace falls through to the app render — even a path with no route. The route handlers do exist in the build (/.next/server/app/.well-known/workflow/v1/flow/route.js) and are listed in app-paths-manifest.json; nothing shadows them (middleware manifest is empty; no .well-known rewrite in next.config.mjs). They simply never execute.

Because the self-call returns 200, the local queue's dispatch logs no error (it only logs on non-2xx), so the failure is completely silent.

Suspected cause & suggested fix

apps/web/package.json:

"build": "next build --turbopack",
"build:web": "next build --turbopack",

Turbopack's production/standalone build does not appear to serve app route handlers under a dot-prefixed top-level segment (.well-known). Suggested fix: build the web app with webpack (drop --turbopack), or have Turbopack emit/serve these routes correctly.

I'm validating a webpack rebuild of this exact image and will follow up / open a PR if it resolves it. Happy to provide the full .workflow-data run dump, manifests, or any other diagnostics.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions