argoproj/argo-workflows · error
artifact key %q must start with %q
Error message
artifact key %q must start with %q
What it means
ValidateUploadedArtifactKey enforces that an artifact key matches the exact format the upload endpoint generates for the caller's namespace: uploads/{namespace}/{uuid}/{filename}. A key that does not begin with the namespace-scoped 'uploads/<namespace>/' prefix is rejected, since client-supplied keys are applied to artifact locations without further ownership checks.
Source
Thrown at server/utils/artifactkey.go:23
"path"
"slices"
"strings"
"github.com/google/uuid"
)
// ValidateUploadedArtifactKey checks that key is exactly the format the upload
// endpoint generates for namespace: uploads/{namespace}/{uuid}/{filename}. It
// rejects path traversal, absolute paths, empty segments, and any key outside
// the upload prefix, since a client-supplied key is otherwise applied to the
// artifact location without further checks.
//
// This is defense-in-depth, not a proof of ownership: a valid-looking key
// naming another user's upload under the same namespace still passes.
func ValidateUploadedArtifactKey(namespace, key string) error {
prefix := "uploads/" + namespace + "/"
if !strings.HasPrefix(key, prefix) {
return fmt.Errorf("artifact key %q must start with %q", key, prefix)
}
if strings.Contains(key, "..") {
return fmt.Errorf("artifact key %q must not contain '..'", key)
}
if strings.HasPrefix(key, "/") {
return fmt.Errorf("artifact key %q must not be an absolute path", key)
}
if path.Clean(key) != key {
return fmt.Errorf("artifact key %q is not in canonical form", key)
}
parts := strings.Split(key, "/")
if len(parts) != 4 {
return fmt.Errorf("artifact key %q must have exactly 4 segments: uploads/{namespace}/{uuid}/{filename}", key)
}
if slices.Contains(parts, "") {
return fmt.Errorf("artifact key %q must not contain empty segments", key)
}View on GitHub (pinned to 35bff19146)
Solutions
- Prefix the key with 'uploads/<your-namespace>/': uploads/{namespace}/{uuid}/{filename}.
- Re-generate the key by letting the upload endpoint produce it instead of constructing it manually.
- Verify the namespace argument matches the namespace encoded in the key (exact, case-sensitive match).
Example fix
// before
err := utils.ValidateUploadedArtifactKey("my-ns", "abc123/results.tar.gz")
// after
key := fmt.Sprintf("uploads/%s/%s/%s", "my-ns", uuid.NewString(), "results.tar.gz")
err := utils.ValidateUploadedArtifactKey("my-ns", key) Defensive patterns
Strategy: validation
Validate before calling
func validUploadKey(namespace, key string) bool {
return strings.HasPrefix(key, "uploads/"+namespace+"/")
}
if !validUploadKey(ns, key) { /* rebuild key before calling */ } Type guard
func isUploadKeyFor(key, namespace string) bool {
return strings.HasPrefix(key, "uploads/"+namespace+"/")
} Try / catch
if err := utils.ValidateUploadedArtifactKey(ns, key); err != nil {
if strings.Contains(err.Error(), "must start with") {
key = path.Join("uploads", ns, key) // or regenerate key
}
} Prevention
- Always build keys as uploads/{namespace}/{uuid}/{filename} using path.Join.
- Never hand-copy keys across namespaces or environments.
- Let the upload endpoint generate the key instead of constructing it manually.
When it happens
Trigger: Passing an artifact key that omits the uploads/<namespace>/ prefix (e.g. 'my-file.bin', 'uploads/other-ns/uuid/file' when namespace differs), a raw bucket key from another environment, or a key built by hand with the wrong namespace string.
Common situations: Hard-coded keys from another namespace; copying a key from logs of a different namespace; forgetting to prepend the prefix when constructing keys programmatically; case mismatch in the namespace.
Related errors
- artifact key %q must not contain '..'
- protocol %s is not allowed
- key unsupported: cannot get key for artifact location, becau
- artifact key %q must not be an absolute path
- artifact key %q is not in canonical form
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/8e5b2dc08f146948.
Report an issue: GitHub.