Tencent/WeKnora · error

unsafe MinIO endpoint: %w

Error message

unsafe MinIO endpoint: %w

What it means

newMinioClient validates the configured MinIO endpoint with utils.ValidateURLForSSRF before constructing the minio-go client, and wraps any validation failure as "unsafe MinIO endpoint: %w". This is a deliberate SSRF guard: endpoints pointing at loopback, link-local, private/metadata addresses, or malformed URLs are rejected before any network connection is made.

Source

Thrown at internal/application/service/file/minio.go:32

	"github.com/Tencent/WeKnora/internal/types/interfaces"
	"github.com/Tencent/WeKnora/internal/utils"
	"github.com/google/uuid"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
)

// minioFileService MinIO file service implementation
type minioFileService struct {
	client     *minio.Client
	bucketName string
}

// newMinioClient creates a bare minioFileService with just the SDK client initialised.
// Shared by NewMinioFileService (which also ensures the bucket exists) and
// CheckMinioConnectivity (read-only probe).
func newMinioClient(endpoint, accessKeyID, secretAccessKey, bucketName string, useSSL bool) (*minioFileService, error) {
	if err := utils.ValidateURLForSSRF(endpoint); err != nil {
		return nil, fmt.Errorf("unsafe MinIO endpoint: %w", err)
	}
	httpConfig := utils.DefaultSSRFSafeHTTPClientConfig()
	client, err := minio.New(endpoint, &minio.Options{
		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
		Secure: useSSL,
		Transport: &utils.SSRFValidatingRoundTripper{
			Base: utils.NewSSRFSafeTransport(httpConfig),
		},
	})
	if err != nil {
		return nil, fmt.Errorf("failed to initialize MinIO client: %w", err)
	}
	return &minioFileService{client: client, bucketName: bucketName}, nil
}

// NewMinioFileService creates a MinIO file service.
// It verifies that the bucket exists and creates it if missing.
func NewMinioFileService(endpoint,

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Set the endpoint to a public, resolvable, non-private host (or an HTTPS domain fronting MinIO) that passes ValidateURLForSSRF
  2. Log/inspect the wrapped cause (errors.Unwrap) to see exactly which SSRF rule failed and correct the URL format accordingly
  3. If MinIO genuinely must live on an internal network, front it with an approved internal gateway/domain that the SSRF allowlist accepts, or extend utils.ValidateURLForSSRF allowlisting deliberately (with security review)
  4. Ensure the env var carries scheme+host consistently (e.g. minio.example.com vs https://minio.example.com) matching what the validator expects

Example fix

// before
MINIO_ENDPOINT=http://127.0.0.1:9000  // rejected: loopback
// after
MINIO_ENDPOINT=https://minio.example.com  // public endpoint, passes SSRF validation
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(endpoint)
if err != nil || u.Host == "" {
    return fmt.Errorf("endpoint must be scheme+host, got %q", endpoint)
}
host := u.Hostname()
ip := net.ParseIP(host)
if host == "localhost" || (ip != nil && (ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsUnspecified())) {
    return fmt.Errorf("endpoint %q points to a private/loopback address and will be rejected by SSRF validation", endpoint)
}

Try / catch

svc, err := file.NewMinioFileService(endpoint, ak, sk, bucket, useSSL)
if err != nil && strings.Contains(err.Error(), "unsafe MinIO endpoint") {
    // fail fast at startup with a clear config message; do not retry
    return fmt.Errorf("invalid MINIO_ENDPOINT %q: %w", endpoint, err)
} else if err != nil {
    return err
}

Prevention

When it happens

Trigger: Calling NewMinioFileService or CheckMinioConnectivity with an endpoint that fails ValidateURLForSSRF: e.g. http://127.0.0.1:9000, http://169.254.169.254, localhost, 10.x/172.16-31.x/192.168.x addresses, missing scheme, or an otherwise invalid URL.

Common situations: Developers pointing MINIO_ENDPOINT at localhost for local development while the SSRF validator rejects private/loopback hosts; misconfigured env var left empty or containing a scheme-less value; Kubernetes setups where MinIO is reached via a cluster-internal .svc address that the validator considers private.

Related errors


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