{"record":{"id":"f2f07c6a7bd7659c","repo":"Billionmail/BillionMail","slug":"failed-to-save-certificate-v","errorCode":null,"errorMessage":"failed to save certificate: %v","messagePattern":"failed to save certificate: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/acme/cli.go","lineNumber":204,"sourceCode":"\n\t// Get certificate info\n\tcertInfo := GetCertInfo(certificate)\n\tfmt.Printf(\"Certificate issued successfully:\\n\")\n\tfmt.Printf(\"  Subject: %s\\n\", certInfo.Subject)\n\tfmt.Printf(\"  Issuer: %s\\n\", certInfo.Issuer)\n\tfmt.Printf(\"  Valid from: %s\\n\", certInfo.NotBefore)\n\tfmt.Printf(\"  Valid to: %s\\n\", certInfo.NotAfter)\n\tfmt.Printf(\"  Domains: %s\\n\", strings.Join(certInfo.DNSNames, \", \"))\n\n\t// Certificate files\n\tcertificatePath := filepath.Join(cli.OutputPath, \"certificate.pem\")\n\tprivateKeyPath := filepath.Join(cli.OutputPath, \"private_key.pem\")\n\n\t// Save certificate and private key to files\n\t_, err = public.WriteFile(certificatePath, certificate)\n\n\tif err != nil {\n\t\treturn \"\", \"\", fmt.Errorf(\"failed to save certificate: %v\", err)\n\t}\n\n\t_, err = public.WriteFile(privateKeyPath, privateKey)\n\n\tif err != nil {\n\t\treturn \"\", \"\", fmt.Errorf(\"failed to save private key: %v\", err)\n\t}\n\n\tfmt.Printf(\"Certificate saved to: %s\\n\", certificatePath)\n\tfmt.Printf(\"Private key saved to: %s\\n\", privateKeyPath)\n\n\treturn certificatePath, privateKeyPath, nil\n}\n\n/**\n * @brief Save certificate to database\n * @return error\n */","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/acme/cli.go#L186-L222","documentation":"Apply throws this when public.WriteFile fails to save certificate.pem into cli.OutputPath after successful issuance, wrapping the OS error with %v. Like errors 111-112 but in the AcmeCLI.Apply path (file name derived from first domain under OutputPath).","triggerScenarios":"Apply with OutputPath set to a non-existent or unwritable directory, or OutputPath empty so relative write fails; the WriteFile for certificatePath returns an error.","commonSituations":"Running the CLI from a cwd without write permission while OutputPath was defaulted; OutputPath directory not created before Apply; read-only container filesystem; disk full.","solutions":["Create OutputPath beforehand (mkdir -p) and ensure the running user can write to it","Pass an absolute, existing --output-path when invoking applyCommand","Fix the wrapped OS error (permissions, disk space) indicated in the message","Use a dedicated certs directory owned by the service user"],"exampleFix":"// before\ncli := &AcmeCLI{Email: e, Domains: d, VerifyType: \"http\", OutputPath: \"\"} // writes to relative cwd\n// after\nout := \"/var/lib/acme/certs\"\nos.MkdirAll(out, 0750)\ncli := &AcmeCLI{Email: e, Domains: d, VerifyType: \"http\", OutputPath: out}","handlingStrategy":"validation","validationCode":"out := cli.OutputPath\nif out == \"\" { return errors.New(\"output path must be set\") }\nif err := os.MkdirAll(out, 0750); err != nil { return err }\nif err := unix.Access(out, unix.W_OK); err != nil { return fmt.Errorf(\"%s not writable: %w\", out, err) }","typeGuard":null,"tryCatchPattern":"if _, _, err := cli.Apply(ctx); err != nil {\n    if strings.Contains(err.Error(), \"failed to save certificate\") {\n        log.Printf(\"cert write failed for %s: %v; check OutputPath permissions/space\", cli.OutputPath, err)\n    }\n    return err\n}","preventionTips":["Always pass an absolute, pre-created OutputPath","Create the output directory at deploy/startup time","Run the CLI as a user with write access to the output dir (avoid defaulting to root-only cwd)","Monitor disk space on the volume holding OutputPath"],"tags":["filesystem","acme","certificate","file-write"],"backgroundTag":"file-write-permission-denied","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}