Summary
When creating a pipeline via the gRPC API or HTTP upload endpoint, a user-provided
name containing uppercase letters (e.g., My-Pipeline) bypasses the
ValidatePipelineName() validation and reaches the Kubernetes backend directly,
resulting in a confusing low-level Kubernetes error rather than a clear input
validation error.
Background: Two Code Paths for Pipeline Name
Path A: No name provided by the user
When neither name nor display_name is provided in the API request, the server
falls back to the pipeline spec file contents. For v2 pipelines, pipelineSpec.name
is parsed from the YAML and passed through ValidatePipelineName() before being
used as pipeline.Name.
Relevant code: resource_manager.go, CreatePipelineAndPipelineVersion():
if tmpl.IsV2() {
pipelineSpecName = tmpl.V2PipelineName()
if err := common.ValidatePipelineName(pipelineSpecName); err != nil {
return nil, nil, err // ← validated early, clear error
}
}
Path B: name explicitly provided by the user (affected path)
When the user explicitly passes a name via HTTP query string (?name=My-Pipeline)
or gRPC request body, toModelPipeline() in api_converter.go uses it directly
without calling ValidatePipelineName(). The name is passed as-is to the store layer.
In the Kubernetes backend (pipeline_store_kubernetes.go), the name becomes
metadata.name of a CRD resource. If it contains uppercase letters, the Kubernetes
API server rejects it with an IsInvalid error, which is then wrapped as:
BadRequestError: Invalid pipeline name: pipeline names must consist of lower case
alphanumeric characters, '-' or '.', and must start and end with an alphanumeric
character (code: 10)
This error message is correct but arrives too late — after an unnecessary round-trip
to the Kubernetes API server, and without indicating where in the code the validation
should have caught it.
Expected Behavior
ValidatePipelineName() (which already exists in backend/src/apiserver/common/utils.go)
should be called on the user-provided name in the pipeline creation path, before
reaching the store layer. This would provide a fast, consistent validation error
regardless of whether the backend is Kubernetes or SQL.
Suggested Fix
In resource_manager.go, CreatePipeline() or CreatePipelineAndPipelineVersion(),
add an early call to common.ValidatePipelineName(p.Name) when the Kubernetes backend
is in use (or unconditionally, since the constraint applies to both backends for
consistency).
Steps to Reproduce
Deploy KFP with Kubernetes pipeline store backend
Call CreatePipeline with name = "My-Pipeline" (uppercase M)
Observe the error comes from the Kubernetes API layer rather than input validation
Environment
Backend: Kubernetes pipeline store (PipelineStoreKubernetes)
Affected API: CreatePipeline (gRPC v2beta1), HTTP upload (?name=)
Not affected: cases where name is derived from pipeline spec (Path A above)
Edit
2026-03-24 Updated analysis after further investigation.
Correction: Where the validation should live
The original suggestion of adding ValidatePipelineName() in resource_manager.go
is incorrect. ResourceManager is backend-agnostic — it serves both the
Kubernetes and SQL backends. The DNS label constraint on metadata.name is
structural to the Kubernetes backend (see pipeline_types.go:54: pipeline.Name
is mapped directly to metadata.name), so the validation belongs in
pipeline_store_kubernetes.go before k.client.Create() is called. This has zero
impact on the SQL backend.
Updated Expected Behavior
When a user provides a name containing uppercase letters or other characters
invalid for a Kubernetes resource name, the error should be returned early from
pipeline_store_kubernetes.go with a clear, actionable message — before any
round-trip to the Kubernetes API server. The message should explicitly guide the
user:
Use name for a Kubernetes-compatible identifier (lowercase alphanumeric and -
only). Use display_name for a human-readable label that may contain uppercase
letters, spaces, or other characters.
Whether the backend should additionally offer auto-normalization
(e.g., silently lowercasing and truncating name to produce a valid K8s name)
is an open design question left to community discretion. If implemented, this
should never be silent — the normalized name must be surfaced to the caller
in the response or as a warning.
Impacted by this bug? Give it a 👍.
Summary
When creating a pipeline via the gRPC API or HTTP upload endpoint, a user-provided
namecontaining uppercase letters (e.g.,My-Pipeline) bypasses theValidatePipelineName()validation and reaches the Kubernetes backend directly,resulting in a confusing low-level Kubernetes error rather than a clear input
validation error.
Background: Two Code Paths for Pipeline Name
Path A: No
nameprovided by the userWhen neither
namenordisplay_nameis provided in the API request, the serverfalls back to the pipeline spec file contents. For v2 pipelines,
pipelineSpec.nameis parsed from the YAML and passed through
ValidatePipelineName()before beingused as
pipeline.Name.Relevant code:
resource_manager.go,CreatePipelineAndPipelineVersion():Path B: name explicitly provided by the user (affected path)
When the user explicitly passes a name via HTTP query string (?name=My-Pipeline)
or gRPC request body, toModelPipeline() in api_converter.go uses it directly
without calling ValidatePipelineName(). The name is passed as-is to the store layer.
In the Kubernetes backend (pipeline_store_kubernetes.go), the name becomes
metadata.name of a CRD resource. If it contains uppercase letters, the Kubernetes
API server rejects it with an IsInvalid error, which is then wrapped as:
BadRequestError: Invalid pipeline name: pipeline names must consist of lower case
alphanumeric characters, '-' or '.', and must start and end with an alphanumeric
character (code: 10)
This error message is correct but arrives too late — after an unnecessary round-trip
to the Kubernetes API server, and without indicating where in the code the validation
should have caught it.
Expected Behavior
ValidatePipelineName() (which already exists in backend/src/apiserver/common/utils.go)
should be called on the user-provided name in the pipeline creation path, before
reaching the store layer. This would provide a fast, consistent validation error
regardless of whether the backend is Kubernetes or SQL.
Suggested Fix
In resource_manager.go, CreatePipeline() or CreatePipelineAndPipelineVersion(),
add an early call to common.ValidatePipelineName(p.Name) when the Kubernetes backend
is in use (or unconditionally, since the constraint applies to both backends for
consistency).
Steps to Reproduce
Deploy KFP with Kubernetes pipeline store backend
Call CreatePipeline with name = "My-Pipeline" (uppercase M)
Observe the error comes from the Kubernetes API layer rather than input validation
Environment
Backend: Kubernetes pipeline store (PipelineStoreKubernetes)
Affected API: CreatePipeline (gRPC v2beta1), HTTP upload (?name=)
Not affected: cases where name is derived from pipeline spec (Path A above)
Edit
2026-03-24 Updated analysis after further investigation.
Correction: Where the validation should live
The original suggestion of adding
ValidatePipelineName()inresource_manager.gois incorrect.
ResourceManageris backend-agnostic — it serves both theKubernetes and SQL backends. The DNS label constraint on
metadata.nameisstructural to the Kubernetes backend (see
pipeline_types.go:54:pipeline.Nameis mapped directly to
metadata.name), so the validation belongs inpipeline_store_kubernetes.gobeforek.client.Create()is called. This has zeroimpact on the SQL backend.
Updated Expected Behavior
When a user provides a
namecontaining uppercase letters or other charactersinvalid for a Kubernetes resource name, the error should be returned early from
pipeline_store_kubernetes.gowith a clear, actionable message — before anyround-trip to the Kubernetes API server. The message should explicitly guide the
user:
Whether the backend should additionally offer auto-normalization
(e.g., silently lowercasing and truncating
nameto produce a valid K8s name)is an open design question left to community discretion. If implemented, this
should never be silent — the normalized name must be surfaced to the caller
in the response or as a warning.
Impacted by this bug? Give it a 👍.