kubernetes/kops · error

invalid Azure Blob location; expected azureblob://<account>/

Error message

invalid Azure Blob location; expected azureblob://<account>/<container>/<key>

What it means

escapeBlobLocation validates that the azureblob:// URL is representable by the account-based blob.core.windows.net URL nodeup downloads from: correct scheme, non-empty host, no port or IPv6-style host, no userinfo, no query, no fragment, and both container and key present. Anything violating these constraints returns this descriptive error so it fails early rather than in the node boot retry loop.

Source

Thrown at pkg/model/resources/nodeup.go:304

		return "", fmt.Errorf("parsing S3 location: %w", err)
	}
	if u.Scheme != "s3" || u.Host == "" {
		return "", fmt.Errorf("invalid S3 location")
	}

	return "s3://" + u.Host + httpbinding.EscapePath(u.Path, false), nil
}

func escapeBlobLocation(location string) (string, error) {
	u, err := url.Parse(location)
	if err != nil {
		return "", fmt.Errorf("parsing Azure Blob location: %w", err)
	}
	container, key, _ := strings.Cut(strings.TrimPrefix(u.Path, "/"), "/")
	// Reject ports, IPv6 hosts, userinfo, queries, and fragments, which the account-based
	// blob.core.windows.net URL cannot represent, so they fail here instead of in the boot retry loop.
	if u.Scheme != "azureblob" || u.Host == "" || u.Hostname() != u.Host || u.User != nil || u.RawQuery != "" || u.Fragment != "" || container == "" || key == "" {
		return "", fmt.Errorf("invalid Azure Blob location; expected azureblob://<account>/<container>/<key>")
	}

	return "azureblob://" + u.Host + httpbinding.EscapePath(u.Path, false), nil
}

func (b *NodeUpScript) Build() (fi.Resource, error) {
	if b.ProxyEnv == nil {
		b.ProxyEnv = funcEmptyString
	}
	if b.EnvironmentVariables == nil {
		b.EnvironmentVariables = funcEmptyString
	}

	if b.useS3Download() && b.S3Region == "" {
		return nil, fmt.Errorf("ResolveS3Region must be called before building a nodeup script with an s3:// source")
	}

	if b.useBlobDownload() {

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Reformat the location as exactly azureblob://<account>/<container>/<key>.
  2. Remove query strings, fragments, ports, and userinfo from the URL.
  3. Ensure the path has both a container and a key segment after the leading slash.
  4. If on a sovereign cloud, note the script only supports the public cloud endpoint — see the AZURE_ENVIRONMENT check (error 1406).

Example fix

// before
NodeUpSource: "azureblob://myaccount/mycontainer/nodeup?sv=2020-02-10&sig=abc"
// after
NodeUpSource: "azureblob://myaccount/mycontainer/artifacts/linux/amd64/nodeup"
Defensive patterns

Strategy: validation

Validate before calling

func validAzureBlob(loc string) error {
    u, err := url.Parse(loc)
    if err != nil { return err }
    c, k, _ := strings.Cut(strings.TrimPrefix(u.Path, "/"), "/")
    if u.Scheme != "azureblob" || u.Host == "" || u.Hostname() != u.Host ||
        u.User != nil || u.RawQuery != "" || u.Fragment != "" || c == "" || k == "" {
        return errors.New("expected azureblob://<account>/<container>/<key>")
    }
    return nil
}

Type guard

func isPlainAzureBlobURL(loc string) bool { return validAzureBlob(loc) == nil }

Try / catch

if err := validAzureBlob(loc); err != nil {
    return fmt.Errorf("reformat blob source as azureblob://account/container/key: %w", err)
}

Prevention

When it happens

Trigger: A nodeup source like 'azureblob://account:443/container/key' (port), 'azureblob://user@account/container/key' (userinfo), 'azureblob://account/container/key?sig=...' (query), 'azureblob://account/container#frag' (fragment), or a URL missing the container or key segment.

Common situations: Copying a full portal/SAS blob URL instead of building the azureblob:// form; using a sovereign-cloud endpoint URL; omitting the container so the path has only one segment.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/c83b57d238316f11. Report an issue: GitHub.