wavetermdev/waveterm · critical

recErr.Error()

Error message

recErr.Error()

What it means

This is the plain-text 500 response emitted by WebFnWrap (pkg/web/web.go:397) when a wrapped HTTP handler panics and the route was registered without JsonErrors. PanicHandler recovers the panic and converts it to an error; http.Error then writes recErr.Error() with HTTP 500. It signals an unexpected bug inside the handler, not a client mistake.

Source

Thrown at pkg/web/web.go:397

	Active bool `json:"active"`
	Open   bool `json:"open"`
}

func WebFnWrap(opts WebFnOpts, fn WebFnType) WebFnType {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			recErr := panichandler.PanicHandler("WebFnWrap", recover())
			if recErr == nil {
				return
			}
			if opts.JsonErrors {
				jsonRtn := marshalReturnValue(nil, recErr)
				w.Header().Set(ContentTypeHeaderKey, ContentTypeJson)
				w.Header().Set(ContentLengthHeaderKey, fmt.Sprintf("%d", len(jsonRtn)))
				w.WriteHeader(http.StatusOK)
				w.Write(jsonRtn)
			} else {
				http.Error(w, recErr.Error(), http.StatusInternalServerError)
			}
		}()
		if !opts.AllowCaching {
			w.Header().Set(CacheControlHeaderKey, CacheControlHeaderNoCache)
		}
		w.Header().Set("Access-Control-Expose-Headers", "X-ZoneFileInfo")

		// Handle CORS preflight OPTIONS requests without auth validation
		if r.Method == http.MethodOptions {
			w.WriteHeader(http.StatusOK)
			return
		}

		err := authkey.ValidateIncomingRequest(r)
		if err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(fmt.Sprintf("error validating authkey: %v", err)))
			return

View on GitHub (pinned to a4447c1563)

Solutions

  1. Read the panic message in the 500 response body and the server log entry from PanicHandler to locate the panicking handler and stack trace.
  2. Fix the underlying nil/ out-of-range bug in the wrapped handler function.
  3. Add defensive checks (nil maps, len() guards, ok-style type assertions) in the handler before dereferencing.
  4. If the client should receive structured JSON errors, register the route with WebFnOpts{JsonErrors: true} so panics are marshaled via marshalReturnValue instead of plain text.
  5. Reproduce locally with a request matching the handler's expected parameters and confirm the 500 no longer occurs.

Example fix

// before: unchecked slice access in a WebFnWrap'd handler
parts := strings.Split(r.URL.Path, "/")
use(parts[2]) // panics on short paths

// after
if len(parts) < 3 {
    http.Error(w, "path is required", http.StatusBadRequest)
    return
}
use(parts[2])
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side pre-check: nothing prevents a server panic, but validate inputs before calling
if (!params.path || params.path.length === 0) throw new Error("path is required");

Try / catch

const resp = await fetch(url, opts);
if (resp.status === 500) {
  const msg = await resp.text();
  throw new Error(`handler panic: ${msg}`);
}
// JSON-errors routes: parse {isError, error} from the 200 body
const body = await resp.json();
if (body.isError) throw new Error(body.error);

Prevention

When it happens

Trigger: Any handler wrapped with WebFnWrap panics (nil dereference, index out of range, failed type assertion) while opts.JsonErrors is false. The panic string becomes the response body with status 500.

Common situations: Developers hit this when a query parameter is missing and the handler indexes into an empty slice, when a JSON field is absent and a cast like r.URL.Query().Get(...) is used unchecked downstream, or after a refactor introduced a nil map/slice access in a web endpoint.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/4be772fe408be07a. Report an issue: GitHub.