Tencent/WeKnora · error

S3 access key and secret key must be provided together

Error message

S3 access key and secret key must be provided together

What it means

newS3Client supports two credential modes: AWS default credential chain (no AK/SK) or explicit static credentials. This error means exactly one of accessKey/secretKey was provided while the other was empty, which is an ambiguous, unsupported configuration.

Source

Thrown at internal/application/service/file/s3.go:46

	bucketName string
	pathPrefix string
}

// newS3Client creates a bare s3FileService with just the SDK client initialised.
func newS3Client(endpoint, accessKey, secretKey, bucketName, region, pathPrefix string, forcePathStyle bool) (*s3FileService, error) {
	if err := utils.ValidateURLForSSRF(endpoint); err != nil {
		return nil, fmt.Errorf("unsafe S3 endpoint: %w", err)
	}
	var cfg aws.Config
	var err error

	// With no explicit AK/SK, keep the AWS default credential chain intact. This
	// supports IAM roles for EC2/ECS/EKS (IRSA), web identity, shared config, and
	// environment credentials without persisting long-lived keys in WeKnora.
	loadOptions := []func(*config.LoadOptions) error{config.WithRegion(region)}
	if accessKey != "" || secretKey != "" {
		if accessKey == "" || secretKey == "" {
			return nil, fmt.Errorf("S3 access key and secret key must be provided together")
		}
		loadOptions = append(loadOptions, config.WithCredentialsProvider(
			credentials.NewStaticCredentialsProvider(accessKey, secretKey, ""),
		))
	}
	cfg, err = config.LoadDefaultConfig(context.Background(), loadOptions...)

	if err != nil {
		return nil, fmt.Errorf("failed to load AWS config: %w", err)
	}

	// Create S3 client with custom endpoint if provided.
	// For S3-compatible services (non-AWS), use path-style addressing
	// (endpoint/bucket/key) instead of virtual-hosted style (bucket.endpoint/key).
	httpClient := utils.NewSSRFSafeHTTPClient(utils.DefaultSSRFSafeHTTPClientConfig())
	var client *s3.Client
	if endpoint != "" {
		usePathStyle := forcePathStyle || !strings.Contains(endpoint, "amazonaws.com")

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Provide both accessKey and secretKey together, or leave both empty to use the AWS default credential chain (IAM role, env, shared config)
  2. Audit your config source — a secret-injection tool likely dropped or blanked one key
  3. If you intend IRSA/instance-profile auth, remove the single leftover key

Example fix

// before
NewS3FileService(endpoint, "AKIA...", "", bucket, region, prefix) // error
// after
NewS3FileService(endpoint, "AKIA...", "secret...", bucket, region, prefix) // or "", "" for default chain
Defensive patterns

Strategy: validation

Validate before calling

if (accessKey == "") != (secretKey == "") {
    return errors.New("S3 access key and secret key must be provided together")
}

Prevention

When it happens

Trigger: Setting S3_ACCESS_KEY without S3_SECRET_KEY (or vice versa) in configuration passed to NewS3FileService / NewS3FileServiceWithOptions / CheckS3ConnectivityWithOptions.

Common situations: Partial config updates where one of the two keys was rotated or redacted by a deployment tool, secret manager returning empty for one key, YAML/env template with only one key filled in.

Related errors


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