{"record":{"id":"8e79bf1d85cdae16","repo":"opentofu/opentofu","slug":"invalid-reference-artifact-tag-or-digest-not-allo","errorCode":null,"errorMessage":"invalid reference: artifact tag or digest not allowed","messagePattern":"invalid reference: artifact tag or digest not allowed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/command/cliconfig/ociauthconfig/repository_addr.go","lineNumber":48,"sourceCode":"\t// implement something similar inline here or find an alternative external library\n\t// to use for this.\n\t// We're actually using the _reference_ parser here, since a reference incorporates\n\t// a repository address, but we'll reject after the fact any result that includes\n\t// a tag or digest portion since we're not intending to accept addresses of specific\n\t// artifacts.\n\n\tif strings.Count(addr, \"/\") != 0 {\n\t\t// This seems to be an address with both a registry and a repository path.\n\t\tref, parseErr := orasregistry.ParseReference(addr)\n\t\tif parseErr != nil {\n\t\t\t// The ORAS function returns errors with sufficient context that any\n\t\t\t// further decoration we might add here would be redundant. For example,\n\t\t\t// this might return an error whose message is\n\t\t\t// \"invalid reference: invalid registry invalid:thing:blah\".\n\t\t\treturn \"\", \"\", parseErr\n\t\t}\n\t\tif ref.Reference != \"\" {\n\t\t\treturn \"\", \"\", fmt.Errorf(\"invalid reference: artifact tag or digest not allowed\")\n\t\t}\n\t\treturn ref.Registry, ref.Repository, nil\n\t}\n\n\t// If we get here then it seems like we have _just_ a domain part. ORAS does\n\t// not have a separate function just for parsing a domain, so we'll borrow the\n\t// validate function from its reference parser instead.\n\tref := &orasregistry.Reference{\n\t\tRegistry: addr,\n\t}\n\terr = ref.ValidateRegistry()\n\t// ValidateRegistry returns an error with a string like \"invalid reference: invalid registry invalid:thing:blah\"\n\treturn addr, \"\", err\n}\n","sourceCodeStart":30,"sourceCodeEnd":63,"githubUrl":"https://github.com/opentofu/opentofu/blob/3561785c48c1ce615e7c50261bd351f26053efa2/internal/command/cliconfig/ociauthconfig/repository_addr.go#L30-L63","documentation":"OpenTofu addresses providers in OCI registries as registry domain plus repository path, with no tag or digest — version selection is the job of version constraints. When parsing an address that contains a '/', orasregistry.ParseReference succeeds but yields a non-empty Reference (tag or digest component), this error rejects it. Registries serve exactly one artifact per repository+version mapping, so a tag would make the mirror target ambiguous.","triggerScenarios":"An oci_mirror 'include' pattern or installation address like registry.example.com/mirror/hashicorp/terraform:1.9.0 or .../terraform@sha256:abcd — the ':tag' or '@digest' suffix makes ref.Reference non-empty and triggers the error.","commonSituations":"Users familiar with docker pull syntax assuming tags are allowed; copying image references from registry UIs; attempting to pin a provider by digest in the mirror configuration.","solutions":["Remove the :tag or @sha256:... suffix from the address — keep only domain/namespace/type","Pin versions where they belong: the provider's version constraint in the Terraform/OpenTofu configuration or the mirror's metadata","Double-check the repository path still has the right number of segments after removing the suffix"],"exampleFix":"# before\noci_mirror {\n  include \"registry.example.com/mirror/terraform:1.9.0\" {}\n}\n# after\noci_mirror {\n  include \"registry.example.com/mirror/terraform\" {}\n}\n# pin in code instead:\nrequired_providers { tofu = { source = \"registry.example.com/mirror/terraform\", version = \"1.9.0\" } }","handlingStrategy":"validation","validationCode":"func hasTagOrDigest(addr string) bool {\n    // strip scheme if present, then look for ':' after the first '/' or '@'\n    if i := strings.Index(addr, \"/\"); i >= 0 {\n        rest := addr[i:]\n        return strings.Contains(rest, \":\") || strings.Contains(addr, \"@\")\n    }\n    return strings.Contains(addr, \":\") || strings.Contains(addr, \"@\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["OCI provider addresses are domain/namespace/type only — keep docker tag habits out of the config","Pin versions with required_providers version constraints, never with mirror addresses","When copying from a registry UI, delete the ':tag' / '@digest' suffix before pasting"],"tags":["oci","address-parsing","provider-mirror","config"],"backgroundTag":null,"analyzedSha":"3561785c48c1ce615e7c50261bd351f26053efa2","analyzedAt":"2026-08-15T23:27:16.226Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}