{"record":{"id":"bc1986fa67f35603","repo":"xai-org/grok-build","slug":"invalid-gcs-url-scheme-expected-gs-got","errorCode":null,"errorMessage":"Invalid GCS URL scheme: expected 'gs', got '{}'","messagePattern":"Invalid GCS URL scheme: expected 'gs', got '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-file-utils/src/gcs.rs","lineNumber":116,"sourceCode":"/// Uploads bytes to cloud storage at the specified path.\n/// Returns the full storage URL on success.\n/// Dispatches to direct, proxy, or S3 backend based on config.\npub async fn upload_bytes<C: StorageConfig>(\n    config: &C,\n    object_path: &str,\n    content: &[u8],\n    content_type: &str,\n) -> anyhow::Result<String> {\n    match config.upload_method() {\n        UploadMethod::Direct {\n            service_account_key,\n        } => {\n            // Parse the bucket URL to extract bucket name (required for direct mode)\n            let url = url::Url::parse(config.bucket_url())\n                .with_context(|| format!(\"Invalid GCS URL: {}\", config.bucket_url()))?;\n\n            if url.scheme() != \"gs\" {\n                anyhow::bail!(\n                    \"Invalid GCS URL scheme: expected 'gs', got '{}'\",\n                    url.scheme()\n                );\n            }\n\n            let bucket = url\n                .host_str()\n                .context(\"GCS URL must have a bucket name\")?\n                .to_string();\n\n            upload_bytes_direct(\n                &bucket,\n                object_path,\n                content,\n                content_type,\n                service_account_key.as_deref(),\n            )\n            .await","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-file-utils/src/gcs.rs#L98-L134","documentation":"upload_bytes (used by upload_bytes_signed) parses the configured bucket_url as a URL and requires the scheme to be 'gs' for direct (non-HTTP) GCS access. If the scheme is anything else (https, s3, or a typo), it bails with this message naming the offending scheme. This guards against misconfigured storage endpoints being handed to the GCS-specific code path.","triggerScenarios":"Calling upload_bytes/upload_bytes_signed with a config whose bucket_url() is set to an https:// URL, an s3:// URL, a bare hostname, or a misspelled scheme (gcs://, GS://).","commonSituations":"Copy-pasting a signed HTTPS console URL into bucket_url config; reusing an S3-style config struct for GCS; environment-variable substitution producing an empty or wrong-prefixed URL; docs examples mixing cloud providers.","solutions":["Set bucket_url to a proper gs:// URL, e.g. gs://my-bucket (optionally with object prefix).","Check the env var/config source feeding bucket_url for typos (gcs://, GS://) — the scheme is compared case-sensitively after URL parsing.","If you have an HTTPS endpoint intentionally (emulator/proxy), use the non-direct upload mode instead of the direct gs:// path.","Validate the URL at startup (parse and assert scheme == \"gs\") so failures surface in config checks, not mid-upload."],"exampleFix":"// before\nlet config = GcsConfig { bucket_url: \"https://storage.googleapis.com/my-bucket\".into(), .. };\nupload_bytes(&config, key, data).await?;\n// after\nlet config = GcsConfig { bucket_url: \"gs://my-bucket\".into(), .. };\nupload_bytes(&config, key, data).await?;","handlingStrategy":"validation","validationCode":"fn validate_gcs_bucket_url(bucket_url: &str) -> Result<(), String> {\n    let url = url::Url::parse(bucket_url).map_err(|e| format!(\"bad URL: {e}\"))?;\n    if url.scheme() != \"gs\" {\n        return Err(format!(\"scheme must be 'gs', got '{}' in {bucket_url}\", url.scheme()));\n    }\n    if url.host_str().unwrap_or_default().is_empty() {\n        return Err(\"missing bucket name\".into());\n    }\n    Ok(())\n}\nvalidate_gcs_bucket_url(config.bucket_url())?; // run at startup, not mid-upload","typeGuard":null,"tryCatchPattern":"match upload_bytes(&config, key, data).await {\n    Err(e) if e.to_string().contains(\"Invalid GCS URL scheme\") => {\n        return Err(anyhow::anyhow!(\n            \"config error: bucket_url={} must be a gs:// URL\",\n            config.bucket_url()\n        ));\n    }\n    other => other,\n}","preventionTips":["Store bucket as a bare name (my-bucket) and construct gs:// at call time","Validate bucket_url scheme at config load/startup","Never paste https:// signed URLs into bucket_url config","Keep per-provider config types separate so S3 URLs can't reach GCS paths","Watch env-var substitution — empty vars yield schemeless URLs"],"tags":["gcs","configuration","url","storage"],"backgroundTag":"invalid-url-scheme","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}