#1197·Vane

Search pipeline hangs/crashes with no error response when model output leaks Harmony-format tokens

Author: sarge18Created Sep 12, 2026Updated Sep 12, 2026

Summary

/api/search in balanced/quality optimization mode crashes server-side, and (as of a follow-up repro on 2026-09-11) the same failure shape also hangs silently in speed mode for at least one query — when the chat model's raw completion leaks Harmony-style special tokens (<|channel|>...<|tool_call|>) into the text Vane's action-dispatch parser reads.

Environment

  • Vane deployed standalone (itzcrazykns1337/vane:slim-latest), fronted by SearXNG (own instance, braveapi engine) and a POST /api/providers OpenAI-compatible provider pointed at an internal LLM gateway (Ollama-backed, gemma4:26b, MoE).
  • Consumed via a custom MCP wrapper (thetom42/perplexica-mcp) calling Vane's plain /api/search REST endpoint.
  • Also reproduced through Open WebUI's native MCP tool-server integration (real browser session, not just direct API calls).

Original crash (balanced/quality mode)

Request body sets optimizationMode: "balanced" (or "quality", or omits optimizationMode — Vane's own server-side default is balanced). Server logs:

Error: Action with name __reasoning_preamble<channel|><|tool_call>call:web_search
not found

This comes from i.research/k.searchAsync in the compiled app/api/search/route.js action-dispatch code. It's an unhandled exception — the HTTP request never gets a response, no error is returned to the caller, and the connection just hangs until the client's own read-timeout gives up and tears down the session.

Root cause: the chat model's completion contains literal Harmony-format tokens (<|channel|>, <|tool_call|>, etc. — the same token family that broke #1066, though via a different code path) that leak through into the plain-text stream Vane's action parser expects to contain only its own action-tag syntax. The parser has no fallback for unrecognized/malformed action names — it throws, and the throw isn't caught anywhere in the request path.

Workaround found: setting optimizationMode: "speed" explicitly avoids this code path entirely and was verified working end-to-end for the query that originally triggered the crash.

Follow-up: same hang shape reproduced in speed mode (2026-09-11)

Re-tested through a real Open WebUI browser session (Playwright-driven, MCP tool toggled on) with optimizationMode still going through the caller's default (now "speed", since the caller-side workaround was applied) and a different query ("What is today's top headline on Hacker News?"). Observed via docker logs ollama-webui:

  • POST .../mcp (the tool call to Vane) succeeds — 200 OK.
  • Then total silence: no further chat-completion activity, no error of any kind.
  • ~3m21s later, the caller's own read-timeout tears down the MCP session (DELETE .../mcp, 200 OK).
  • The assistant's message in the UI stays completely empty the entire time.

No corresponding runtime request logging appeared in docker logs/kubectl logs for the Vane container itself in either case — Vane does not appear to log anything at the request level once past startup, which is consistent with #1172/#997 ("errors aren't surfaced/logged").

This means either:

  1. The speed-mode workaround doesn't fully avoid the Harmony-token-leak code path for every query, or
  2. This is a distinct silent hang with the same "unhandled promise rejection swallowed somewhere in the request path, connection just never resolves" shape.

Either way, the underlying bug is the same class: Vane's server-side request handling has no top-level error boundary for the search pipeline, so a bad/unexpected model output (or possibly some other transient failure) becomes a permanently-hanging HTTP request instead of a 500 with a message.

Suggested fix

  1. Wrap the search pipeline (i.research/k.searchAsync and whatever handles speed mode) in a try/catch that always resolves the HTTP response, even on unexpected internal errors — turn "hangs forever" into "returns 500 with a message," which alone would make this whole class of bug debuggable from the outside (also addresses #1172/#997).
  2. Make the action-dispatch parser tolerant of unrecognized action names/leaked model special-tokens instead of throwing on them — sanitize or strip <|...|>-style tokens before parsing, or treat an unparseable action as "no action" rather than a hard error.

Reproduction

Available on request — have full timestamped log evidence (docker logs ollama-webui, provider/model config) for both the original crash and the 2026-09-11 follow-up hang.