{"record":{"id":"3c938277e6e9c9ab","repo":"Billionmail/BillionMail","slug":"create-output-dir-w-3c9382","errorCode":null,"errorMessage":"create output dir: %w","messagePattern":"create output dir: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/video_gen/voice.go","lineNumber":170,"sourceCode":"\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(resp.Body)\n\t\treturn nil, fmt.Errorf(\"cartesia clone API error %d: %s\", resp.StatusCode, string(body))\n\t}\n\n\tvar result VoiceCloneResponse\n\tif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode clone response: %w\", err)\n\t}\n\treturn &result, nil\n}\n\n// TextToSpeech generates audio from text using a Cartesia voice.\n// Returns the path to the output WAV file.\nfunc TextToSpeech(ctx context.Context, cfg VoiceConfig, voiceID, transcript, filename string) (string, error) {\n\tif err := os.MkdirAll(cfg.OutputDir, 0755); err != nil {\n\t\treturn \"\", fmt.Errorf(\"create output dir: %w\", err)\n\t}\n\n\treq := TTSRequest{\n\t\tVoiceID:      voiceID,\n\t\tTranscript:   transcript,\n\t\tModelID:      \"sonic-2\",\n\t\tOutputFormat: DefaultTTSOutputFormat(),\n\t\tLanguage:     \"en\",\n\t}\n\n\thttpReq, err := BuildTTSRequest(cfg, req)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\thttpReq = httpReq.WithContext(ctx)\n\n\tresp, err := cfg.doHTTP(httpReq)\n\tif err != nil {","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/voice.go#L152-L188","documentation":"TextToSpeech first ensures cfg.OutputDir exists via os.MkdirAll(dir, 0755) before writing any audio. If the directory cannot be created (permission denied, path is an existing file, read-only filesystem, invalid path), the OS error is wrapped as 'create output dir'. It is a local filesystem failure, not a Cartesia problem.","triggerScenarios":"cfg.OutputDir points inside a directory the process cannot write (e.g. /root/... when running unprivileged), OutputDir path exists as a regular file, read-only container filesystem, or a path with invalid characters.","commonSituations":"Docker containers mounting volumes read-only, running service as non-root user while OutputDir defaults to a root-owned path, typo'd OutputDir config, disk-full or SELinux/AppArmor denial.","solutions":["Check OutputDir is writable by the process user: ls -ld and touch a test file.","Verify OutputDir is not an existing regular file; fix the config value.","Ensure the container/volume is mounted read-write and has free space.","Create the directory ahead of time with correct ownership (mkdir -p + chown).","Pre-validate OutputDir in DefaultVoiceConfig / startup config checks."],"exampleFix":"// before\nif err := os.MkdirAll(cfg.OutputDir, 0755); err != nil {\n    return \"\", fmt.Errorf(\"create output dir: %w\", err)\n}\n// after\nif info, err := os.Stat(cfg.OutputDir); err == nil && !info.IsDir() {\n    return \"\", fmt.Errorf(\"output dir %s is a file\", cfg.OutputDir)\n}\nif err := os.MkdirAll(cfg.OutputDir, 0o755); err != nil {\n    return \"\", fmt.Errorf(\"create output dir %s: %w\", cfg.OutputDir, err)\n}","handlingStrategy":"validation","validationCode":"func ensureWritableDir(dir string) error {\n    info, err := os.Stat(dir)\n    if err == nil && !info.IsDir() {\n        return fmt.Errorf(\"%s exists and is not a directory\", dir)\n    }\n    if err := os.MkdirAll(dir, 0o755); err != nil {\n        return err\n    }\n    probe := filepath.Join(dir, \".write-test\")\n    if err := os.WriteFile(probe, nil, 0o644); err != nil {\n        return err\n    }\n    return os.Remove(probe)\n}\n// call before TextToSpeech:\nif err := ensureWritableDir(cfg.OutputDir); err != nil {\n    return fmt.Errorf(\"output dir unusable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"path, err := video_gen.TextToSpeech(ctx, cfg, voiceID, transcript, filename)\nif err != nil {\n    if strings.Contains(err.Error(), \"create output dir\") || strings.Contains(err.Error(), \"create output file\") {\n        log.Printf(\"filesystem problem with %s: %v\", cfg.OutputDir, err)\n        // fix config/permissions, then retry\n    }\n    return err\n}","preventionTips":["Default OutputDir to a path writable by the service user (e.g. /var/lib/app/audio)","Mount container volumes read-write with correct ownership","Validate OutputDir at startup, not at first TTS call","Monitor free disk space on the audio volume"],"tags":["filesystem","permissions","io"],"backgroundTag":"permission-denied","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}