{"record":{"id":"bb10b41b9b0aaaf9","repo":"googleapis/mcp-toolbox","slug":"bucket-q-is-not-allowed-by-source-q-configuratio","errorCode":null,"errorMessage":"bucket %q is not allowed by source %q configuration","messagePattern":"bucket %q is not allowed by source %q configuration","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sources/cloudstorage/cloudstorage.go","lineNumber":104,"sourceCode":"}\n\nvar _ sources.Source = &Source{}\n\ntype Source struct {\n\tConfig\n\tclient *storage.Client\n}\n\nfunc (s *Source) validateBucket(bucket string) error {\n\tif len(s.AllowedBuckets) == 0 {\n\t\treturn nil\n\t}\n\tfor _, b := range s.AllowedBuckets {\n\t\tif b == bucket {\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn fmt.Errorf(\"bucket %q is not allowed by source %q configuration\", bucket, s.Name)\n}\n\n// validateLocalPath enforces allowedLocalRoots. The path must sit under an\n// allowed root both as written and after symlinks are resolved: the first check\n// keeps the rejection message tied to what the caller actually asked for, and\n// the second is what makes the root a real boundary, since a symlink planted\n// under a root can otherwise point anywhere on the filesystem.\n//\n// The resolved comparison uses resolved roots as well, so an allowed root that\n// is itself reached through a symlink (/tmp on macOS, a symlinked workspace)\n// still matches.\nfunc (s *Source) validateLocalPath(p string) error {\n\tclean, err := cloudstoragecommon.ValidateLocalPath(p)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(s.AllowedLocalRoots) == 0 {\n\t\treturn nil","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/sources/cloudstorage/cloudstorage.go#L86-L122","documentation":"CloudStorage source implements an allowlist: if 'allowedBuckets' is configured, every bucket-touching operation (list, read, create, metadata, IAM policy) is checked against it. A request for a bucket not on the list is rejected before any GCS API call. If the list is empty, all buckets are permitted.","triggerScenarios":"Calling ListObjects, ReadObject, CreateBucket, GetBucketMetadata, GetBucketIAMPolicy, or GetObjectMetadata with a bucket name that is not an exact (case-sensitive) entry in the source's allowedBuckets list while that list is non-empty.","commonSituations":"LLM agent requests a bucket that exists in GCP but was never allowlisted in the toolbox config; typos or case mismatch between config and actual bucket name; buckets added in GCP after the config was written.","solutions":["Add the requested bucket name (exact spelling) to 'allowedBuckets' in the cloud-storage source config and restart the toolbox.","Check for case/typo mismatch between the requested name and the allowlist entry.","If all buckets should be accessible, remove the allowedBuckets field (empty list permits everything)."],"exampleFix":"# before\nallowedBuckets: [my-bucket-a]\n# after\nallowedBuckets: [my-bucket-a, my-bucket-b]","handlingStrategy":"validation","validationCode":"allowed := map[string]bool{\"my-bucket-a\": true, \"my-bucket-b\": true}\nif !allowed[bucketName] {\n    return fmt.Errorf(\"bucket %q must be added to allowedBuckets\", bucketName)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep allowedBuckets in sync with the buckets the agent will use","Match bucket names exactly — the check is case-sensitive and untyped","Constrain tool descriptions so agents only request allowlisted buckets","Review the allowlist when adding buckets in GCP"],"tags":["authorization","cloud-storage","allowlist","configuration","access-control"],"backgroundTag":"resource-not-allowed","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}