{"record":{"id":"1f0b79827409c7df","repo":"netbirdio/netbird","slug":"create-output-file-w","errorCode":null,"errorMessage":"create output file: %w","messagePattern":"create output file: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/capture.go","lineNumber":167,"sourceCode":"\t\t\treturn handleCaptureError(err)\n\t\t}\n\t\tif _, err := out.Write(pkt.GetData()); err != nil {\n\t\t\treturn fmt.Errorf(\"write output: %w\", err)\n\t\t}\n\t}\n}\n\n// captureOutput returns the writer for capture data and a cleanup function\n// that finalizes the file. Errors from the cleanup must be propagated.\nfunc captureOutput(cmd *cobra.Command) (io.Writer, func() error, error) {\n\toutPath, _ := cmd.Flags().GetString(\"output\")\n\tif outPath == \"\" {\n\t\treturn os.Stdout, func() error { return nil }, nil\n\t}\n\n\tf, err := os.CreateTemp(filepath.Dir(outPath), filepath.Base(outPath)+\".*.tmp\")\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"create output file: %w\", err)\n\t}\n\ttmpPath := f.Name()\n\treturn f, func() error {\n\t\tvar merr *multierror.Error\n\t\tif err := f.Close(); err != nil {\n\t\t\tmerr = multierror.Append(merr, fmt.Errorf(\"close output file: %w\", err))\n\t\t}\n\t\tfi, statErr := os.Stat(tmpPath)\n\t\tif statErr != nil || fi.Size() == 0 {\n\t\t\tif rmErr := os.Remove(tmpPath); rmErr != nil && !os.IsNotExist(rmErr) {\n\t\t\t\tmerr = multierror.Append(merr, fmt.Errorf(\"remove empty output file: %w\", rmErr))\n\t\t\t}\n\t\t\treturn nberrors.FormatErrorOrNil(merr)\n\t\t}\n\t\tif err := os.Rename(tmpPath, outPath); err != nil {\n\t\t\tmerr = multierror.Append(merr, fmt.Errorf(\"rename output file: %w\", err))\n\t\t\treturn nberrors.FormatErrorOrNil(merr)\n\t\t}","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/capture.go#L149-L185","documentation":"captureOutput creates the pcap output as a temp file in the same directory as the requested --output path (basename + .*.tmp) via os.CreateTemp, and this wraps that failure. Creating in the target directory is deliberate — the finalize step renames the temp file onto the final path atomically, so a temp file on another filesystem would break the rename. The error means the directory itself could not host a new file.","triggerScenarios":"--output points into a directory that does not exist (netbird debug capture -o /var/captures/x.pcap with /var/captures missing); no write permission on the directory (running unprivileged, writing to /root or a root-owned dir); read-only or full filesystem (EROFS, ENOSPC); -o with a bare filename while cwd is not writable.","commonSituations":"Typo in the output path; expecting the tool to mkdir -p parent directories (it does not); running as non-root where the CLI default dirs are root-owned; container/CI with a read-only volume as the output location.","solutions":["Create the parent directory first: mkdir -p /var/captures, then rerun with -o /var/captures/x.pcap","Check write permission on the directory (ls -ld, id) and choose a writable path or run with appropriate privileges","Verify the filesystem is not read-only or full: mount | grep <dir>, df -h <dir>","Prefer an absolute path for -o to avoid cwd surprises"],"exampleFix":"# before: parent directory does not exist\nnetbird debug capture -o /var/captures/x.pcap\n\n# after: create it first\nmkdir -p /var/captures && netbird debug capture -o /var/captures/x.pcap","handlingStrategy":"validation","validationCode":"// Verify writability of the output directory before starting the capture:\ndir := filepath.Dir(outPath)\nif info, err := os.Stat(dir); err != nil || !info.IsDir() {\n    return fmt.Errorf(\"output directory %s missing\", dir)\n}\nprobe, err := os.CreateTemp(dir, \".probe\")\nif err != nil {\n    return fmt.Errorf(\"cannot write in %s: %v\", dir, err)\n}\nos.Remove(probe.Name())","typeGuard":null,"tryCatchPattern":null,"preventionTips":["mkdir -p the output directory as part of any wrapper script","Always pass an absolute file path to -o; never a directory","Prefer /var/tmp or a data dir over /tmp for long-lived captures"],"tags":["capture","filesystem","cli","permissions","output"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}