{"record":{"id":"f544f19949da5b02","repo":"docker/cli","slug":"error-establishing-connection-to-trust-repository","errorCode":null,"errorMessage":"error establishing connection to trust repository: %w","messagePattern":"error establishing connection to trust repository: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/docker-trust/internal/trust/trust_push.go","lineNumber":104,"sourceCode":"\t}\n\n\tif err := jsonstream.Display(ctx, in, ioStreams.Out(), jsonstream.WithAuxCallback(handleTarget)); err != nil {\n\t\treturn err\n\t}\n\n\tif cnt > 1 {\n\t\treturn errors.New(\"internal error: only one call to handleTarget expected\")\n\t}\n\n\tif notaryTarget == nil {\n\t\treturn errors.New(\"no targets found, provide a specific tag in order to sign it\")\n\t}\n\n\t_, _ = fmt.Fprintln(ioStreams.Out(), \"Signing and pushing trust metadata\")\n\n\trepo, err := GetNotaryRepository(ioStreams.In(), ioStreams.Out(), userAgent, repoInfo, &authConfig, \"push\", \"pull\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error establishing connection to trust repository: %w\", err)\n\t}\n\n\t// get the latest repository metadata so we can figure out which roles to sign\n\t_, err = repo.ListTargets()\n\n\tswitch err.(type) {\n\tcase client.ErrRepoNotInitialized, client.ErrRepositoryNotExist:\n\t\tkeys := repo.GetCryptoService().ListKeys(data.CanonicalRootRole)\n\t\tvar rootKeyID string\n\t\t// always select the first root key\n\t\tif len(keys) > 0 {\n\t\t\tsort.Strings(keys)\n\t\t\trootKeyID = keys[0]\n\t\t} else {\n\t\t\trootPublicKey, err := repo.GetCryptoService().Create(data.CanonicalRootRole, \"\", data.ECDSAKey)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cmd/docker-trust/internal/trust/trust_push.go#L86-L122","documentation":"In PushTrustedReference (trust_push.go:102-104), GetNotaryRepository failed to construct the authenticated notary client/transport, and the error is wrapped with 'error establishing connection to trust repository'. GetNotaryRepository (trust.go:119-201) builds the TLS config, reads the cert directory, pings the notary /v2/ endpoint, and sets up token/basic auth - any of those failing yields this wrap.","triggerScenarios":"The /v2/ ping to the notary server fails or the challenge manager cannot parse the response; TLS handshake fails because the server cert is untrusted or the local tls/<host> cert directory has bad certs; DOCKER_CONTENT_TRUST_SERVER is set to a non-https URL (rejected by Server()); registry auth credentials are missing so the token handler cannot authenticate; certificateDirectory url.Parse fails.","commonSituations":"Self-hosted notary server with a self-signed cert that is not installed under ~/.docker/tls/<host>; DOCKER_CONTENT_TRUST_SERVER typo or http:// (must be https); behind a corporate proxy that blocks the notary endpoint; 'docker login' credentials expired so token auth returns 401; clock skew invalidating TLS; DNS resolution failure for the notary host.","solutions":["Verify DOCKER_CONTENT_TRUST_SERVER is a valid https URL and reachable: curl -vk <server>/v2/ should return 200 or 401.","For a private notary with a self-signed cert, place the CA cert at ~/.docker/tls/<host>/ca.crt (and client.crt/client.key if mTLS) and retry.","Re-authenticate to the registry: docker login <registry> so the token handler has valid credentials.","Check network/proxy: ensure HTTPS_PROXY/HTTP_PROXY allow traffic to the notary server and DNS resolves the host.","If repoInfo.Index.Secure is false, the client skips TLS verification (InsecureSkipVerify) - confirm that is intentional rather than masking a cert problem."],"exampleFix":"# before: self-signed notary cert, connection fails\nexport DOCKER_CONTENT_TRUST_SERVER=https://notary.internal:4443\nDOCKER_CONTENT_TRUST=1 docker push registry.internal/img:tag\n# after: install the CA so TLS verifies\nmkdir -p ~/.docker/tls/notary.internal:4443\ncp /etc/notary/root-ca.crt ~/.docker/tls/notary.internal:4443/ca.crt\nDOCKER_CONTENT_TRUST=1 docker push registry.internal/img:tag","handlingStrategy":"try-catch","validationCode":"// Validate notary server reachability and TLS before the real push.\nfunc preflightNotaryServer(server string, authCfg *registrytypes.AuthConfig) error {\n    if server == \"\" {\n        return errors.New(\"DOCKER_CONTENT_TRUST_SERVER is empty\")\n    }\n    u, err := url.Parse(server)\n    if err != nil || u.Scheme != \"https\" {\n        return fmt.Errorf(\"trust server must be https, got %s\", server)\n    }\n    client := &http.Client{Timeout: 5 * time.Second}\n    req, _ := http.NewRequest(http.MethodGet, server+\"/v2/\", nil)\n    if authCfg.Username != \"\" {\n        req.SetBasicAuth(authCfg.Username, authCfg.Password)\n    }\n    resp, err := client.Do(req)\n    if err != nil {\n        return fmt.Errorf(\"cannot reach notary server %s: %w\", server, err)\n    }\n    resp.Body.Close()\n    return nil\n}","typeGuard":null,"tryCatchPattern":"repo, err := trust.GetNotaryRepository(in, out, ua, repoInfo, authCfg, \"push\", \"pull\")\nif err != nil {\n    return fmt.Errorf(\"error establishing connection to trust repository: %w\", err)\n}","preventionTips":["Install the notary server CA under ~/.docker/tls/<host>:<port>/ca.crt for private registries.","Always set DOCKER_CONTENT_TRUST_SERVER to an https URL.","Run 'docker login' before trust operations so the token handler can authenticate.","Add a connectivity preflight (curl /v2/) in CI before trusted pushes.","Ensure corporate proxies allow traffic to the notary host."],"tags":["docker","notary","content-trust","network","tls","authentication","configuration"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}