wavetermdev/waveterm · warning

%s

Error message

%s

What it means

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).

Source

Thrown at pkg/aiusechat/openai/openai-backend.go:42

	"github.com/wavetermdev/waveterm/pkg/web/sse"
)

// sanitizeHostnameInError removes the Wave cloud hostname from error messages
func sanitizeHostnameInError(err error) error {
	if err == nil {
		return nil
	}

	errStr := err.Error()
	parsedURL, parseErr := url.Parse(uctypes.DefaultAIEndpoint)
	if parseErr == nil && parsedURL.Host != "" {
		if strings.Contains(errStr, parsedURL.Host) {
			errStr = strings.ReplaceAll(errStr, uctypes.DefaultAIEndpoint, "AI service")
			errStr = strings.ReplaceAll(errStr, parsedURL.Host, "host")
		}
	}

	return fmt.Errorf("%s", errStr)
}

// ---------- OpenAI wire types (subset) ----------

type OpenAIChatMessage struct {
	MessageId          string                         `json:"messageid"` // internal field for idempotency (cannot send to openai)
	Message            *OpenAIMessage                 `json:"message,omitempty"`
	FunctionCall       *OpenAIFunctionCallInput       `json:"functioncall,omitempty"`
	FunctionCallOutput *OpenAIFunctionCallOutputInput `json:"functioncalloutput,omitempty"`
	Usage              *OpenAIUsage
}

type OpenAIMessage struct {
	Role    string                 `json:"role"`
	Content []OpenAIMessageContent `json:"content"`
}

type OpenAIFunctionCallInput struct {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Read the sanitized message ("AI service"/"host") and diagnose the underlying call (connectivity, auth, request payload) rather than the hostname
  2. Check network connectivity/proxy settings and API credentials to the AI endpoint
  3. If you need the raw error (e.g. for errors.Is), capture it before this layer or match on substring, not sentinel errors
  4. Add logging upstream of sanitizeHostnameInError to keep the unsanitized error internally

Example fix

// before
if errors.Is(err, net.ErrClosed) { ... } // never matches after sanitization
// after
var netErr net.Error
if errors.As(err, &netErr) { ... } // or check err.Error() substrings
Defensive patterns

Strategy: fallback

Try / catch

err := RunOpenAIChatStep(ctx, sse, chatOpts, cont)
if err != nil {
    // message is host-sanitized; log internally and surface generic guidance
    log.Printf("openai step failed: %v", err)
    return fmt.Errorf("AI request failed; check connectivity/credentials")
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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