{"record":{"id":"07a96f8db2d9261c","repo":"wavetermdev/waveterm","slug":"s-07a96f","errorCode":null,"errorMessage":"%s","messagePattern":"%s","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/aiusechat/openai/openai-backend.go","lineNumber":42,"sourceCode":"\t\"github.com/wavetermdev/waveterm/pkg/web/sse\"\n)\n\n// sanitizeHostnameInError removes the Wave cloud hostname from error messages\nfunc sanitizeHostnameInError(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\terrStr := err.Error()\n\tparsedURL, parseErr := url.Parse(uctypes.DefaultAIEndpoint)\n\tif parseErr == nil && parsedURL.Host != \"\" {\n\t\tif strings.Contains(errStr, parsedURL.Host) {\n\t\t\terrStr = strings.ReplaceAll(errStr, uctypes.DefaultAIEndpoint, \"AI service\")\n\t\t\terrStr = strings.ReplaceAll(errStr, parsedURL.Host, \"host\")\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%s\", errStr)\n}\n\n// ---------- OpenAI wire types (subset) ----------\n\ntype OpenAIChatMessage struct {\n\tMessageId          string                         `json:\"messageid\"` // internal field for idempotency (cannot send to openai)\n\tMessage            *OpenAIMessage                 `json:\"message,omitempty\"`\n\tFunctionCall       *OpenAIFunctionCallInput       `json:\"functioncall,omitempty\"`\n\tFunctionCallOutput *OpenAIFunctionCallOutputInput `json:\"functioncalloutput,omitempty\"`\n\tUsage              *OpenAIUsage\n}\n\ntype OpenAIMessage struct {\n\tRole    string                 `json:\"role\"`\n\tContent []OpenAIMessageContent `json:\"content\"`\n}\n\ntype OpenAIFunctionCallInput struct {","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/openai/openai-backend.go#L24-L60","documentation":"sanitizeHostnameInError in pkg/aiusechat/openai/openai-backend.go:42 rewraps a sanitized error string via fmt.Errorf(\"%s\", errStr). It fires when the original error text contained the Wave AI service endpoint hostname, which is replaced with \"AI service\"/\"host\" for user display. The error itself is a pass-through of an underlying OpenAI/network error; this wrapper only masks the host. Because it rewraps with %s, the original error chain and sentinel values are lost (errors.Is/As will not match).","triggerScenarios":"Any error returned by the OpenAI HTTP step in RunOpenAIChatStep whose message contains the parsed host of uctypes.DefaultAIEndpoint (e.g. connection refused, DNS failure, 4xx/5xx response text referencing the endpoint) is passed through sanitizeHostnameInError and re-emitted as a generic fmt.Errorf string.","commonSituations":"Network/DNS outages, proxy misconfiguration, expired auth against the Wave AI endpoint, or any API error surfacing the backend URL in its message; developers comparing errors with errors.Is or matching on the raw hostname get no match after sanitization.","solutions":["Read the sanitized message (\"AI service\"/\"host\") and diagnose the underlying call (connectivity, auth, request payload) rather than the hostname","Check network connectivity/proxy settings and API credentials to the AI endpoint","If you need the raw error (e.g. for errors.Is), capture it before this layer or match on substring, not sentinel errors","Add logging upstream of sanitizeHostnameInError to keep the unsanitized error internally"],"exampleFix":"// before\nif errors.Is(err, net.ErrClosed) { ... } // never matches after sanitization\n// after\nvar netErr net.Error\nif errors.As(err, &netErr) { ... } // or check err.Error() substrings","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := RunOpenAIChatStep(ctx, sse, chatOpts, cont)\nif err != nil {\n    // message is host-sanitized; log internally and surface generic guidance\n    log.Printf(\"openai step failed: %v\", err)\n    return fmt.Errorf(\"AI request failed; check connectivity/credentials\")\n}","preventionTips":["Monitor connectivity to the AI endpoint separately so sanitized messages still map to actionable alerts","Capture raw errors in internal logs before sanitization for debugging","Do not rely on errors.Is/As through this layer; match on message content if needed","Keep secrets/hostnames out of user-facing paths by keeping sanitization in place"],"tags":["go","error-sanitization","openai","network"],"backgroundTag":"hostname-sanitized-error","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}