{"record":{"id":"bcf6e3c1c94be591","repo":"gastownhall/beads","slug":"az-url-must-include-a-storage-account-hostname","errorCode":null,"errorMessage":"az:// URL must include a storage account hostname","messagePattern":"az:// URL must include a storage account hostname","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/remotecache/url.go","lineNumber":162,"sourceCode":"\t\t\treturn fmt.Errorf(\"dolthub:// URL must have org/repo format (e.g., dolthub://myorg/myrepo)\")\n\t\t}\n\tcase \"https\", \"http\", \"git+https\", \"git+http\":\n\t\tif parsed.Host == \"\" {\n\t\t\treturn fmt.Errorf(\"%s:// URL must include a hostname\", scheme)\n\t\t}\n\tcase \"ssh\", \"git\", \"git+ssh\":\n\t\tif parsed.Host == \"\" {\n\t\t\treturn fmt.Errorf(\"%s:// URL must include a hostname\", scheme)\n\t\t}\n\tcase \"s3\", \"aws\", \"gs\":\n\t\t// s3://bucket/path, aws://bucket/path, gs://bucket/path — host is the bucket\n\t\tif parsed.Host == \"\" {\n\t\t\treturn fmt.Errorf(\"%s:// URL must include a bucket name\", scheme)\n\t\t}\n\tcase \"az\":\n\t\t// az://account.blob.core.windows.net/container/path\n\t\tif parsed.Host == \"\" {\n\t\t\treturn fmt.Errorf(\"az:// URL must include a storage account hostname\")\n\t\t}\n\tcase \"oci\":\n\t\tif parsed.Host == \"\" {\n\t\t\treturn fmt.Errorf(\"oci:// URL must include a namespace or bucket host\")\n\t\t}\n\tcase \"file\":\n\t\t// file:// is allowed with any path\n\tcase \"git+file\":\n\t\t// git+file:// is Dolt's normalized form for local git remotes.\n\t}\n\n\treturn nil\n}\n\n// validateSCPURL validates an SCP-style URL (user@host:path)\nfunc validateSCPURL(rawURL string) error {\n\t// Already matched gitSSHPattern, so structure is valid.\n\t// Extract host and verify no control chars (already checked above).","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/remotecache/url.go#L144-L180","documentation":"az:// URLs must carry an Azure storage account hostname of the form account.blob.core.windows.net (az://account.blob.core.windows.net/container/path). An empty host means no storage account was specified.","triggerScenarios":"ValidateRemoteURL with \"az://\", \"az:///container/path\", or a URL where the account hostname was dropped.","commonSituations":"Copying only the container/path portion from an Azure portal URL, or empty ACCOUNT_NAME substitution in config.","solutions":["Use the full account hostname: \"az://myaccount.blob.core.windows.net/container/path\"","Verify AZURE_STORAGE_ACCOUNT substitution produced the account.blob.core.windows.net host","Include the container name after the host"],"exampleFix":"// before\nremote := \"az:///mycontainer/db\"\n// after\nremote := \"az://myaccount.blob.core.windows.net/mycontainer/db\"","handlingStrategy":"validation","validationCode":"func validAzureURL(u string) bool {\n\trest, ok := strings.CutPrefix(u, \"az://\")\n\tif !ok { return false }\n\thost := rest\n\tif i := strings.Index(rest, \"/\"); i >= 0 { host = rest[:i] }\n\treturn strings.Contains(host, \".blob.core.windows.net\")\n}","typeGuard":"func azureBlobURL(s string) (account string, ok bool) {\n\trest, has := strings.CutPrefix(s, \"az://\")\n\tif !has { return \"\", false }\n\tif i := strings.Index(rest, \"/\"); i >= 0 { rest = rest[:i] }\n\tacct, suffix, found := strings.Cut(rest, \".\")\n\treturn acct, found && strings.HasSuffix(suffix, \"blob.core.windows.net\")\n}","tryCatchPattern":"if err := remotecache.ValidateRemoteURL(u); err != nil {\n\tif strings.Contains(err.Error(), \"storage account hostname\") {\n\t\treturn fmt.Errorf(\"azure remote %q must be az://account.blob.core.windows.net/container/path\", u)\n\t}\n}","preventionTips":["Build az URLs from AZURE_STORAGE_ACCOUNT: fmt.Sprintf(\"az://%s.blob.core.windows.net/%s/%s\", acct, container, path)","Assert the account env var is set before formatting","Validate the host suffix .blob.core.windows.net at config load"],"tags":["url-validation","azure","blob-storage"],"backgroundTag":"invalid-remote-url-format","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}