{"record":{"id":"bcb8524e1422b9f0","repo":"wavetermdev/waveterm","slug":"terminput-is-required","errorCode":null,"errorMessage":"terminput is required","messagePattern":"terminput is required","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"tsunami/engine/serverhandlers.go","lineNumber":428,"sourceCode":"\t}\n\n\tbody, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"failed to read request body: %v\", err), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tvar event vdom.VDomEvent\n\tif err := json.Unmarshal(body, &event); err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"failed to parse JSON: %v\", err), http.StatusBadRequest)\n\t\treturn\n\t}\n\tif strings.TrimSpace(event.WaveId) == \"\" {\n\t\thttp.Error(w, \"waveid is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif event.TermInput == nil {\n\t\thttp.Error(w, \"terminput is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\th.renderLock.Lock()\n\th.Client.Root.Event(event, h.Client.GlobalEventHandler)\n\th.renderLock.Unlock()\n\n\tw.WriteHeader(http.StatusNoContent)\n}\n\nfunc (h *httpHandlers) handleDynContent(w http.ResponseWriter, r *http.Request) {\n\tdefer func() {\n\t\tpanicErr := util.PanicHandler(\"handleDynContent\", recover())\n\t\tif panicErr != nil {\n\t\t\thttp.Error(w, fmt.Sprintf(\"internal server error: %v\", panicErr), http.StatusInternalServerError)\n\t\t}\n\t}()\n","sourceCodeStart":410,"sourceCodeEnd":446,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/engine/serverhandlers.go#L410-L446","documentation":"Besides waveId, the event must contain a TermInput payload — that is what this endpoint dispatches via h.Client.Root.Event. If event.TermInput is nil after JSON parsing, the handler returns 400 'terminput is required'.","triggerScenarios":"POSTing an event with a valid waveId but no termInput object, termInput explicitly null, or posting an event type meant for a different endpoint (e.g. a vdom event that is not terminal input).","commonSituations":"Copy-pasted payload templates where the termInput block was deleted; sending UI events (clicks, keys from other blocks) to the term-input endpoint by mistake; serializers omitting nil fields and the field never being set.","solutions":["Include a termInput object in the JSON body (e.g. {\"inputData\":\"...\"})","Confirm you are calling the correct endpoint for your event type","Inspect VDomEvent's TermInput struct shape and fill required sub-fields","If building events in Go, ensure the *TermInput pointer is non-nil before marshaling"],"exampleFix":"// before\n{\"waveId\":\"wave-1234\"}\n// after\n{\"waveId\":\"wave-1234\",\"termInput\":{\"inputData\":\"ls\\n\"}}","handlingStrategy":"validation","validationCode":"if (event.termInput == null) {\n  throw new Error('terminput is required: this endpoint only accepts terminal-input events')\n}","typeGuard":"function hasTermInput(e: unknown): e is { termInput: { inputData: string } } {\n  return (e as any)?.termInput != null && typeof (e as any).termInput === 'object'\n}","tryCatchPattern":"const res = await fetch(url, opts)\nif (res.status === 400 && (await res.text()).includes('terminput is required')) {\n  // route the event to the correct endpoint instead\n}","preventionTips":["Verify the event type matches the endpoint before POSTing","Keep a typed event builder so TermInput cannot be omitted","Add compile-time/type checks for required fields"],"tags":["http","validation","missing-field"],"backgroundTag":"missing-required-argument","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}