{"record":{"id":"8971a089926579f1","repo":"GoogleContainerTools/skaffold","slug":"v-is-not-a-valid-gcs-path","errorCode":null,"errorMessage":"%v is not a valid GCS path","messagePattern":"(.+?) is not a valid GCS path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/skaffold/kubernetes/manifest/gcs.go","lineNumber":49,"sourceCode":"type GCSClient interface {\n\t// Downloads the content that match the given src uri and subfolders.\n\tDownloadRecursive(ctx context.Context, src, dst string) error\n}\n\nvar GetGCSClient = func() GCSClient {\n\treturn &client.Native{}\n}\n\n// DownloadFromGCS downloads all provided manifests from a remote GCS bucket,\n// and returns a relative path pointing to the GCS temp dir.\nfunc DownloadFromGCS(manifests []string) (string, error) {\n\tdir := filepath.Join(ManifestTmpDir, ManifestsFromGCS)\n\tif err := os.MkdirAll(dir, os.ModePerm); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create the tmp directory: %w\", err)\n\t}\n\tfor _, manifest := range manifests {\n\t\tif manifest == \"\" || !strings.HasPrefix(manifest, gcsPrefix) {\n\t\t\treturn \"\", fmt.Errorf(\"%v is not a valid GCS path\", manifest)\n\t\t}\n\t\tgcs := GetGCSClient()\n\t\tif err := gcs.DownloadRecursive(context.Background(), manifest, dir); err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to download manifests fom GCS: %w\", err)\n\t\t}\n\t}\n\treturn ManifestTmpDir, nil\n}\n","sourceCodeStart":31,"sourceCodeEnd":58,"githubUrl":"https://github.com/GoogleContainerTools/skaffold/blob/a1189de023efc32d4b8e11f395acc678aa555011/pkg/skaffold/kubernetes/manifest/gcs.go#L31-L58","documentation":"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.","triggerScenarios":"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://...'.","commonSituations":"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.","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"],"exampleFix":"// before\nremoteManifests := []string{\"my-bucket/manifests\"}       // missing scheme\nremoteManifests := []string{\"\"}                          // empty entry\n// after\nremoteManifests := []string{\"gs://my-bucket/manifests\"}","handlingStrategy":"validation","validationCode":"import \"strings\"\nfunc allGCSURIs(manifests []string) bool {\n  for _, m := range manifests {\n    if m == \"\" || !strings.HasPrefix(m, \"gs://\") { return false }\n  }\n  return true\n}\n// guard: if !allGCSURIs(remoteManifests) { fix config before DownloadFromGCS }","typeGuard":"isGCSPath := func(s string) bool {\n  return strings.HasPrefix(s, \"gs://\") && len(s) > len(\"gs://\")\n}","tryCatchPattern":"if path, err := manifest.DownloadFromGCS(manifests); err != nil {\n  if strings.Contains(err.Error(), \"not a valid GCS path\") {\n    // log offending entries, filter them out, retry\n    return fmt.Errorf(\"skipping non-GCS manifest entries: %w\", err)\n  }\n  return err\n}","preventionTips":["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"],"tags":["gcs","validation","path"],"backgroundTag":"invalid-storage-uri","analyzedSha":"a1189de023efc32d4b8e11f395acc678aa555011","analyzedAt":"2026-09-05T12:09:27.064Z","contentChangedAt":"2026-09-05T12:09:27.064Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}