Tencent/WeKnora · error

service unreachable: %w

Error message

service unreachable: %w

What it means

verifyCredentials executes the health-check request through an SSRF-safe HTTP client with a 10s timeout. Any transport-level failure (DNS failure, connection refused, TLS error, timeout) is logged and wrapped as 'service unreachable: %w'. The credentials were never validated because the request never got a response.

Source

Thrown at internal/application/service/weknoracloud.go:81

		return fmt.Errorf("create verification request failed: %w", err)
	}

	requestID := fmt.Sprintf("verify-%d", time.Now().UnixNano())
	signHeaders := modelsutils.Sign(appID, appSecret, requestID, "{}")
	for k, v := range signHeaders {
		req.Header.Set(k, v)
	}

	logger.Infof(ctx, "credential verification request: method=GET url=%s app_id=%s request_id=%s ",
		healthURL, appID, requestID)

	clientCfg := utils.DefaultSSRFSafeHTTPClientConfig()
	clientCfg.Timeout = 10 * time.Second
	client := utils.NewSSRFSafeHTTPClient(clientCfg)
	resp, err := client.Do(req)
	if err != nil {
		logger.Warnf(ctx, "credential verification HTTP failed: url=%s err=%v", healthURL, err)
		return fmt.Errorf("service unreachable: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {
		return fmt.Errorf("invalid APPID or APPSECRET (HTTP %d)", resp.StatusCode)
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("invalid response status code: %d", resp.StatusCode)
	}
	return nil
}

// CheckStatus 检查 WeKnoraCloud 凭证是否可正常解密
func (s *weKnoraCloudService) CheckStatus(ctx context.Context) (*types.WeKnoraCloudStatusResult, error) {
	tenantID := types.MustTenantIDFromContext(ctx)

	tenant, err := s.tenantRepo.GetTenantByID(ctx, tenantID)
	if err != nil || tenant == nil {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Curl the health endpoint from the same host: curl -v <baseURL>/api/v1/health to confirm reachability.
  2. Check the wrapped cause: DNS vs refused vs timeout tells you which layer is broken.
  3. Verify the SSRF-safe client's allowlist — internal/private addresses may be intentionally blocked; use the public endpoint or adjust policy.
  4. If timeouts are marginal, investigate network latency; the 10s budget is fixed in code.
  5. Confirm the WeKnora Cloud service is running and its port is exposed.

Example fix

// before
clientCfg.Timeout = 10 * time.Second // hard-coded
// after
// keep 10s, but surface retries for transient errors
for attempt := 0; attempt < 3; attempt++ {
	if err := s.verifyCredentials(ctx, appID, appSecret); err == nil || !errors.Is(err, context.DeadlineExceeded) {
		return err
	}
	time.Sleep(time.Second)
}
return nil
Defensive patterns

Strategy: retry

Validate before calling

conn, err := net.DialTimeout("tcp", host+":443", 5*time.Second)
if err != nil {
	return fmt.Errorf("WeKnora Cloud unreachable before credential save: %w", err)
}
conn.Close()

Try / catch

err := svc.SaveCredentials(ctx, appID, appSecret)
if err != nil && strings.Contains(err.Error(), "service unreachable") {
	// transient: retry with backoff, then report outage
	return retryWithBackoff(3, 2*time.Second, func() error {
		return svc.SaveCredentials(ctx, appID, appSecret)
	})
}

Prevention

When it happens

Trigger: SaveCredentials -> verifyCredentials where the WeKnora Cloud host is down, the hostname doesn't resolve, the SSRF-safe client blocks a private/disallowed address, or the request exceeds the 10-second timeout.

Common situations: Wrong base URL pointing at a non-existent host; WeKnora Cloud service stopped; firewall/egress rules blocking the app; pointing at an internal IP that the SSRF-safe client deliberately refuses; slow network exceeding the 10s timeout.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/eab8cd31e3426f80. Report an issue: GitHub.