{"record":{"id":"005286371fa02359","repo":"netbirdio/netbird","slug":"failed-to-write-output-files-w","errorCode":null,"errorMessage":"failed to write output files: %w","messagePattern":"failed to write output files: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/signer/revocation.go","lineNumber":113,"sourceCode":"\nfunc handleCreateRevocationList(cmd *cobra.Command, revocationListFile string, privateRootKeyFile string) error {\n\tprivKeyPEM, err := os.ReadFile(privateRootKeyFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read private root key file: %w\", err)\n\t}\n\n\tprivateRootKey, err := reposign.ParseRootKey(privKeyPEM)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse private root key: %w\", err)\n\t}\n\n\trlBytes, sigBytes, err := reposign.CreateRevocationList(*privateRootKey, expirationDuration)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create revocation list: %w\", err)\n\t}\n\n\tif err := writeOutputFiles(revocationListFile, revocationListFile+\".sig\", rlBytes, sigBytes); err != nil {\n\t\treturn fmt.Errorf(\"failed to write output files: %w\", err)\n\t}\n\n\tcmd.Println(\"✅ Revocation list created successfully\")\n\treturn nil\n}\n\nfunc handleExtendRevocationList(cmd *cobra.Command, keyID, revocationListFile, privateRootKeyFile string) error {\n\tprivKeyPEM, err := os.ReadFile(privateRootKeyFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read private root key file: %w\", err)\n\t}\n\n\tprivateRootKey, err := reposign.ParseRootKey(privKeyPEM)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse private root key: %w\", err)\n\t}\n\n\trlBytes, err := os.ReadFile(revocationListFile)","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/signer/revocation.go#L95-L131","documentation":"Returned when writeOutputFiles cannot persist the freshly created revocation list and its .sig sidecar (client/cmd/signer/revocation.go:212-219). It calls os.WriteFile with 0600 permissions on the --revocation-list-file path and on that path plus \".sig\". The error wraps an *os.PathError whose errno pinpoints the cause: ENOENT (parent directory missing), EACCES (no write permission), EISDIR (path names a directory), ENOSPC (disk full), EROFS (read-only filesystem).","triggerScenarios":"create-revocation-list with a --revocation-list-file whose parent directory does not exist, is not writable by the invoking user, names a directory, or sits on a full or read-only filesystem.","commonSituations":"Typing an output path into a directory that was never created; running as a non-root user against /etc or a release directory owned by root; CI containers with a small tmpfs; a stale read-only mount.","solutions":["Create the parent directory: mkdir -p $(dirname <list-path>)","Check write permission on that directory and fix ownership, or run under an account that has access","Verify the path names a file, not a directory, and the filesystem is mounted read-write","Free space or extend the volume when the PathError shows 'no space left on device'"],"exampleFix":"# before\nsigner create-revocation-list --revocation-list-file /etc/netbird/revocation.list --private-root-key root.pem\n# error: failed to write output files: open /etc/netbird/revocation.list: no such file or directory\n\n# after\nmkdir -p /etc/netbird\nsigner create-revocation-list --revocation-list-file /etc/netbird/revocation.list --private-root-key root.pem","handlingStrategy":"validation","validationCode":"func ensureWritableDir(path string) error {\n    dir := filepath.Dir(path)\n    info, err := os.Stat(dir)\n    if err != nil {\n        return fmt.Errorf(\"stat %s: %w\", dir, err)\n    }\n    if !info.IsDir() {\n        return fmt.Errorf(\"%s is not a directory\", dir)\n    }\n    f, err := os.CreateTemp(dir, \".writecheck-*\")\n    if err != nil {\n        return fmt.Errorf(\"%s not writable: %w\", dir, err)\n    }\n    f.Close()\n    os.Remove(f.Name())\n    return nil\n}\n\n// before running create-revocation-list:\n// err := ensureWritableDir(revocationListFile)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["mkdir -p the output directory before the command in setup scripts","Run the signer as a user that owns the output directory","Monitor free space on the signing host so ENOSPC never hits mid-run"],"tags":["go","cli","file-io","permissions","reposign"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}