{"record":{"id":"7fbcaf222a1b18e6","repo":"Billionmail/BillionMail","slug":"create-output-dir-w-7fbcaf","errorCode":null,"errorMessage":"create output dir: %w","messagePattern":"create output dir: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/video_gen/screenshots.go","lineNumber":56,"sourceCode":"\n// DefaultScreenshotConfig returns config with sensible defaults.\nfunc DefaultScreenshotConfig(websiteURL, businessName, outputDir string) ScreenshotConfig {\n\treturn ScreenshotConfig{\n\t\tWebsiteURL:   websiteURL,\n\t\tBusinessName: businessName,\n\t\tOutputDir:    outputDir,\n\t\tWidth:        1920,\n\t\tHeight:       1080,\n\t\tTimeout:      30 * time.Second,\n\t}\n}\n\n// CaptureScreenshots takes 3 screenshots of the prospect's web presence:\n// homepage, contact/scheduling page, and Google Maps listing.\n// Requires: npx playwright (Node.js + Playwright installed).\nfunc CaptureScreenshots(ctx context.Context, cfg ScreenshotConfig) (*ScreenshotResult, error) {\n\tif err := os.MkdirAll(cfg.OutputDir, 0755); err != nil {\n\t\treturn nil, fmt.Errorf(\"create output dir: %w\", err)\n\t}\n\n\tresult := &ScreenshotResult{\n\t\tHomepage: filepath.Join(cfg.OutputDir, \"homepage.png\"),\n\t\tContact:  filepath.Join(cfg.OutputDir, \"contact.png\"),\n\t\tGoogle:   filepath.Join(cfg.OutputDir, \"google.png\"),\n\t}\n\n\t// Build the Playwright script that captures all 3 screenshots\n\tscript := buildPlaywrightScript(cfg, result)\n\n\ttimeoutCtx, cancel := context.WithTimeout(ctx, cfg.Timeout)\n\tdefer cancel()\n\n\tcmd := exec.CommandContext(timeoutCtx, \"node\", \"-e\", script)\n\tcmd.Env = append(os.Environ(), \"PLAYWRIGHT_BROWSERS_PATH=0\")\n\tout, err := cmd.CombinedOutput()\n\tif err != nil {","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/screenshots.go#L38-L74","documentation":"CaptureScreenshots first ensures cfg.OutputDir exists via os.MkdirAll. If that fails (permissions, read-only filesystem, path is a file, invalid path), it wraps the OS error with 'create output dir: %w'.","triggerScenarios":"os.MkdirAll(cfg.OutputDir, 0755) returns an error: parent dirs not writable, OutputDir exists as a regular file, disk full, or a relative path that resolves outside a read-only container layer.","commonSituations":"Running the pipeline in Docker with a read-only root filesystem and no volume mounted for output; OutputDir pointing at an existing file; running as non-root user without write permission to the configured path.","solutions":["Check the wrapped OS error (permission denied / not a directory / no space) for the exact cause","Ensure the output directory path is writable by the process user (chown/chmod or run with correct UID)","Mount a writable volume at the output path in containerized deployments","Verify OutputDir is a directory path, not an existing file"],"exampleFix":"// before\ncfg := ScreenshotConfig{OutputDir: \"/var/lib/app/shots\"}\n// after\nif err := os.MkdirAll(\"/var/lib/app/shots\", 0o755); err != nil {\n\tlog.Fatalf(\"output dir not writable: %v\", err)\n}\ncfg := ScreenshotConfig{OutputDir: \"/var/lib/app/shots\"}","handlingStrategy":"validation","validationCode":"func ensureWritableDir(path string) error {\n\tinfo, err := os.Stat(path)\n\tif err == nil && !info.IsDir() {\n\t\treturn fmt.Errorf(\"%s is a file, not a directory\", path)\n\t}\n\tif err := os.MkdirAll(path, 0o755); err != nil {\n\t\treturn err\n\t}\n\tf, err := os.CreateTemp(path, \".writecheck\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.Close(); os.Remove(f.Name())\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"res, err := CaptureScreenshots(ctx, cfg)\nif err != nil && strings.HasPrefix(err.Error(), \"create output dir:\") {\n\tvar perr *fs.PathError\n\tif errors.As(err, &perr) && errors.Is(perr.Err, fs.ErrPermission) {\n\t\treturn fmt.Errorf(\"fix permissions or volume mount for %s\", cfg.OutputDir)\n\t}\n}","preventionTips":["Mount a writable volume for all output directories in Docker","Run the process with a UID that owns the output directory","Pre-create output dirs in the deployment/Dockerfile","Use absolute paths for OutputDir to avoid surprises from working-directory changes"],"tags":["filesystem","permissions","directory","io"],"backgroundTag":"filesystem-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"}