{"record":{"id":"61fe1c6af865ebaa","repo":"Billionmail/BillionMail","slug":"create-output-file-w-61fe1c","errorCode":null,"errorMessage":"create output file: %w","messagePattern":"create output file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/video_gen/voice.go","lineNumber":201,"sourceCode":"\t\treturn \"\", err\n\t}\n\thttpReq = httpReq.WithContext(ctx)\n\n\tresp, err := cfg.doHTTP(httpReq)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"cartesia TTS API call: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(resp.Body)\n\t\treturn \"\", fmt.Errorf(\"cartesia TTS API error %d: %s\", resp.StatusCode, string(body))\n\t}\n\n\toutPath := filepath.Join(cfg.OutputDir, filename)\n\tf, err := os.Create(outPath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"create output file: %w\", err)\n\t}\n\tdefer f.Close()\n\n\tif _, err := io.Copy(f, resp.Body); err != nil {\n\t\treturn \"\", fmt.Errorf(\"write audio data: %w\", err)\n\t}\n\n\treturn outPath, nil\n}\n","sourceCodeStart":183,"sourceCodeEnd":211,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/voice.go#L183-L211","documentation":"After a successful TTS response, TextToSpeech creates the destination file at filepath.Join(cfg.OutputDir, filename) with os.Create. If file creation fails — bad directory, permission denied, invalid filename characters, name too long, or disk full — the OS error is wrapped as 'create output file'.","triggerScenarios":"OutputDir exists but is not writable, filename contains '/' or illegal characters (e.g. derived from user input), path exceeds NAME_MAX, or the filesystem is full/read-only.","commonSituations":"Filenames built from user-supplied transcripts/timestamps with slashes or control chars, running as non-root against root-owned output dir, small tmpfs filling up during bulk generation.","solutions":["Log the full outPath and wrapped OS error (os.PathError reveals the cause).","Sanitize filename: strip path separators and illegal characters before joining.","Ensure OutputDir is writable by the service user (chown/chmod or run as correct user).","Check free disk space (df) and filesystem mount options (ro?).","Use a collision-safe name (filepath.Base the input, add unique suffix)."],"exampleFix":"// before\noutPath := filepath.Join(cfg.OutputDir, filename)\nf, err := os.Create(outPath)\nif err != nil {\n    return \"\", fmt.Errorf(\"create output file: %w\", err)\n}\n// after\nsafe := strings.Map(func(r rune) rune {\n    if r == '/' || r == '\\\\' || r == 0 || unicode.IsControl(r) { return -1 }\n    return r\n}, filename)\noutPath := filepath.Join(cfg.OutputDir, safe)\nf, err := os.Create(outPath)\nif err != nil {\n    return \"\", fmt.Errorf(\"create output file %s: %w\", outPath, err)\n}","handlingStrategy":"validation","validationCode":"func sanitizeFilename(name string) (string, error) {\n    name = filepath.Base(name)\n    name = strings.Map(func(r rune) rune {\n        if r == '/' || r == '\\\\' || r == 0 || unicode.IsControl(r) { return -1 }\n        return r\n    }, name)\n    if name == \"\" || name == \".\" || len(name) > 255 {\n        return \"\", fmt.Errorf(\"invalid filename %q\", name)\n    }\n    return name, nil\n}\nsafe, err := sanitizeFilename(filename)\nif err != nil {\n    return err\n}\n// also ensure dir is writable (see error 492 validation)","typeGuard":null,"tryCatchPattern":"path, err := video_gen.TextToSpeech(ctx, cfg, voiceID, transcript, filename)\nif err != nil {\n    if strings.Contains(err.Error(), \"create output file\") {\n        var pathErr *os.PathError\n        if errors.As(err, &pathErr) {\n            log.Printf(\"cannot create %s: %v\", pathErr.Path, pathErr.Err)\n        }\n        return err\n    }\n    return err\n}","preventionTips":["Never pass raw user input as filename; sanitize and add a unique prefix","Run the service with write access to OutputDir","Check disk space before bulk TTS generation","Use filepath.Join and never let filename contain separators"],"tags":["filesystem","io","permissions"],"backgroundTag":"file-create-failed","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"}