JuliusBrussee/caveman · error

objectstore: access key and secret key are required

Error message

objectstore: access key and secret key are required

What it means

validateConfig requires non-empty AccessKey and SecretKey credentials for the S3 backend. Without them the MinIO client cannot authenticate, so configuration fails fast with this error before any connection attempt.

Solutions

  1. Set both S3_ACCESS_KEY (or S3_ACCESS_KEY_ID) and S3_SECRET_ACCESS_KEY in the environment or Config fields.
  2. Verify the secret volume/env mapping actually reaches the process (kubectl exec env / docker inspect).
  3. Check for empty-string values after credential rotation or templating — whitespace-only also fails.
  4. If credentials are legitimately not needed (anonymous bucket), this store still requires them; supply machine credentials.

Example fix

// before
# S3_ACCESS_KEY unset
// after
export S3_ACCESS_KEY_ID="caveman-svc"
export S3_SECRET_ACCESS_KEY="$SECRET_VALUE"
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(cfg.AccessKey) == "" || strings.TrimSpace(cfg.SecretKey) == "" {
    return errors.New("S3 credentials must be set")
}

Try / catch

store, err := objectstore.New(cfg)
if err != nil {
    return fmt.Errorf("object store credentials: %w", err)
}

Prevention

When it happens

Trigger: Calling New/FromEnv with empty AccessKey or SecretKey (env vars S3_ACCESS_KEY / S3_SECRET_ACCESS_KEY or AWS-conventional S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY unset); whitespace-only credential values (TrimSpace'd); a secret manager that injected empty strings.

Common situations: Deploying with the endpoint/bucket set but the credential secret not mounted; rotating credentials and leaving an env var empty; using AWS-style variable names without knowing both naming conventions are accepted.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/38f85a22c0459674. Report an issue: GitHub.

Appendix: source

Thrown at shared/platform/objectstore/objectstore.go:193

		timeout := time.Duration(env.Int("CAVE_OBJECTSTORE_PROBE_TIMEOUT_MS", 10000)) * time.Millisecond
		if timeout <= 0 {
			timeout = 10 * time.Second
		}
		ctx, cancel := context.WithTimeout(context.Background(), timeout)
		defer cancel()
		if err := Probe(ctx, store); err != nil {
			return nil, fmt.Errorf("objectstore: production read/write/delete probe failed: %w", err)
		}
	}
	return store, nil
}

func validateConfig(cfg Config, production bool) error {
	if strings.TrimSpace(cfg.Endpoint) == "" || strings.TrimSpace(cfg.Bucket) == "" {
		return errors.New("objectstore: endpoint and bucket are required")
	}
	if strings.TrimSpace(cfg.AccessKey) == "" || strings.TrimSpace(cfg.SecretKey) == "" {
		return errors.New("objectstore: access key and secret key are required")
	}
	if !production {
		return nil
	}
	if !cfg.UseSSL || strings.HasPrefix(strings.ToLower(strings.TrimSpace(cfg.Endpoint)), "http://") {
		return errors.New("objectstore: production requires TLS")
	}
	raw := strings.TrimSpace(cfg.Endpoint)
	if !strings.Contains(raw, "://") {
		raw = "https://" + raw
	}
	u, err := url.Parse(raw)
	if err != nil || u.Scheme != "https" || u.Hostname() == "" || u.User != nil || u.RawQuery != "" || u.Fragment != "" || (u.Path != "" && u.Path != "/") {
		return errors.New("objectstore: production endpoint must be an HTTPS origin without credentials, path, query, or fragment")
	}
	return nil
}

View on GitHub (pinned to 3ee70a1026)