GoogleContainerTools/skaffold · error
%v is not a valid GCS path
Error message
%v is not a valid GCS path
What it means
DownloadFromGCS requires every entry in the manifests slice to be a GCS URI; entries that are empty or lack the 'gs://' prefix (gcsPrefix) are rejected with '%v is not a valid GCS path'. This guards the GCS client from being handed a local path or garbage.
Source
Thrown at pkg/skaffold/kubernetes/manifest/gcs.go:49
type GCSClient interface {
// Downloads the content that match the given src uri and subfolders.
DownloadRecursive(ctx context.Context, src, dst string) error
}
var GetGCSClient = func() GCSClient {
return &client.Native{}
}
// DownloadFromGCS downloads all provided manifests from a remote GCS bucket,
// and returns a relative path pointing to the GCS temp dir.
func DownloadFromGCS(manifests []string) (string, error) {
dir := filepath.Join(ManifestTmpDir, ManifestsFromGCS)
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create the tmp directory: %w", err)
}
for _, manifest := range manifests {
if manifest == "" || !strings.HasPrefix(manifest, gcsPrefix) {
return "", fmt.Errorf("%v is not a valid GCS path", manifest)
}
gcs := GetGCSClient()
if err := gcs.DownloadRecursive(context.Background(), manifest, dir); err != nil {
return "", fmt.Errorf("failed to download manifests fom GCS: %w", err)
}
}
return ManifestTmpDir, nil
}
View on GitHub (pinned to a1189de023)
Solutions
- Ensure each manifest entry is a full 'gs://bucket/path' URI, including the gs:// scheme
- Remove or filter out empty strings from the manifests list before calling
- Fix typos like 'gcs://' or 's3://' — only 'gs://' is accepted
- If local manifests must be supported, handle them separately instead of passing them to DownloadFromGCS
Example fix
// before
remoteManifests := []string{"my-bucket/manifests"} // missing scheme
remoteManifests := []string{""} // empty entry
// after
remoteManifests := []string{"gs://my-bucket/manifests"} Defensive patterns
Strategy: validation
Validate before calling
import "strings"
func allGCSURIs(manifests []string) bool {
for _, m := range manifests {
if m == "" || !strings.HasPrefix(m, "gs://") { return false }
}
return true
}
// guard: if !allGCSURIs(remoteManifests) { fix config before DownloadFromGCS } Type guard
isGCSPath := func(s string) bool {
return strings.HasPrefix(s, "gs://") && len(s) > len("gs://")
} Try / catch
if path, err := manifest.DownloadFromGCS(manifests); err != nil {
if strings.Contains(err.Error(), "not a valid GCS path") {
// log offending entries, filter them out, retry
return fmt.Errorf("skipping non-GCS manifest entries: %w", err)
}
return err
} Prevention
- Always include the gs:// scheme in remote manifest entries in skaffold.yaml
- Filter empty strings from config lists after env substitution
- Distinguish local vs remote manifests before calling download helpers
- Validate remote-manifest config at startup with a prefix check
When it happens
Trigger: Calling manifest.DownloadFromGCS with a manifests slice containing an empty string, a local file path (e.g. 'manifests/', '/abs/path.yaml'), an https URL, or a 'gcs://' typo instead of 'gs://...'.
Common situations: Misconfigured remote manifests in skaffold config (missing gs:// prefix), env-substitution producing empty strings, or mixing local and remote manifests in a list intended to be all-GCS.
Related errors
- cannot parse URI %q: %w
- URI scheme is %q, must be 'gs'
- %s is a directory
- cannot add an empty image value
- unable to lookup minikube executable. Please add it to PATH
AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05).
Data as JSON: /api/errors/8971a089926579f1.
Report an issue: GitHub.