{"record":{"id":"bdaeebd9837ac7fe","repo":"Tencent/WeKnora","slug":"s3-access-key-and-secret-key-must-be-provided-toge","errorCode":null,"errorMessage":"S3 access key and secret key must be provided together","messagePattern":"S3 access key and secret key must be provided together","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/file/s3.go","lineNumber":46,"sourceCode":"\tbucketName string\n\tpathPrefix string\n}\n\n// newS3Client creates a bare s3FileService with just the SDK client initialised.\nfunc newS3Client(endpoint, accessKey, secretKey, bucketName, region, pathPrefix string, forcePathStyle bool) (*s3FileService, error) {\n\tif err := utils.ValidateURLForSSRF(endpoint); err != nil {\n\t\treturn nil, fmt.Errorf(\"unsafe S3 endpoint: %w\", err)\n\t}\n\tvar cfg aws.Config\n\tvar err error\n\n\t// With no explicit AK/SK, keep the AWS default credential chain intact. This\n\t// supports IAM roles for EC2/ECS/EKS (IRSA), web identity, shared config, and\n\t// environment credentials without persisting long-lived keys in WeKnora.\n\tloadOptions := []func(*config.LoadOptions) error{config.WithRegion(region)}\n\tif accessKey != \"\" || secretKey != \"\" {\n\t\tif accessKey == \"\" || secretKey == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"S3 access key and secret key must be provided together\")\n\t\t}\n\t\tloadOptions = append(loadOptions, config.WithCredentialsProvider(\n\t\t\tcredentials.NewStaticCredentialsProvider(accessKey, secretKey, \"\"),\n\t\t))\n\t}\n\tcfg, err = config.LoadDefaultConfig(context.Background(), loadOptions...)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load AWS config: %w\", err)\n\t}\n\n\t// Create S3 client with custom endpoint if provided.\n\t// For S3-compatible services (non-AWS), use path-style addressing\n\t// (endpoint/bucket/key) instead of virtual-hosted style (bucket.endpoint/key).\n\thttpClient := utils.NewSSRFSafeHTTPClient(utils.DefaultSSRFSafeHTTPClientConfig())\n\tvar client *s3.Client\n\tif endpoint != \"\" {\n\t\tusePathStyle := forcePathStyle || !strings.Contains(endpoint, \"amazonaws.com\")","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/file/s3.go#L28-L64","documentation":"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.","triggerScenarios":"Setting S3_ACCESS_KEY without S3_SECRET_KEY (or vice versa) in configuration passed to NewS3FileService / NewS3FileServiceWithOptions / CheckS3ConnectivityWithOptions.","commonSituations":"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.","solutions":["Provide both accessKey and secretKey together, or leave both empty to use the AWS default credential chain (IAM role, env, shared config)","Audit your config source — a secret-injection tool likely dropped or blanked one key","If you intend IRSA/instance-profile auth, remove the single leftover key"],"exampleFix":"// before\nNewS3FileService(endpoint, \"AKIA...\", \"\", bucket, region, prefix) // error\n// after\nNewS3FileService(endpoint, \"AKIA...\", \"secret...\", bucket, region, prefix) // or \"\", \"\" for default chain","handlingStrategy":"validation","validationCode":"if (accessKey == \"\") != (secretKey == \"\") {\n    return errors.New(\"S3 access key and secret key must be provided together\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Store AK/SK as a paired secret (single secret-manager entry)","Validate credential pairs in config-loading code before calling NewS3FileService","Prefer the default credential chain (IAM roles) over static keys","Add startup config linting that flags half-set credentials"],"tags":["s3","config","credentials","validation"],"backgroundTag":"missing-credential-pair","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}