Billionmail/BillionMail · error
create output file: %w
Error message
create output file: %w
What it means
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'.
Source
Thrown at core/internal/service/video_gen/voice.go:201
return "", err
}
httpReq = httpReq.WithContext(ctx)
resp, err := cfg.doHTTP(httpReq)
if err != nil {
return "", fmt.Errorf("cartesia TTS API call: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return "", fmt.Errorf("cartesia TTS API error %d: %s", resp.StatusCode, string(body))
}
outPath := filepath.Join(cfg.OutputDir, filename)
f, err := os.Create(outPath)
if err != nil {
return "", fmt.Errorf("create output file: %w", err)
}
defer f.Close()
if _, err := io.Copy(f, resp.Body); err != nil {
return "", fmt.Errorf("write audio data: %w", err)
}
return outPath, nil
}
View on GitHub (pinned to fc36c76c05)
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).
Example fix
// before
outPath := filepath.Join(cfg.OutputDir, filename)
f, err := os.Create(outPath)
if err != nil {
return "", fmt.Errorf("create output file: %w", err)
}
// after
safe := strings.Map(func(r rune) rune {
if r == '/' || r == '\\' || r == 0 || unicode.IsControl(r) { return -1 }
return r
}, filename)
outPath := filepath.Join(cfg.OutputDir, safe)
f, err := os.Create(outPath)
if err != nil {
return "", fmt.Errorf("create output file %s: %w", outPath, err)
} Defensive patterns
Strategy: validation
Validate before calling
func sanitizeFilename(name string) (string, error) {
name = filepath.Base(name)
name = strings.Map(func(r rune) rune {
if r == '/' || r == '\\' || r == 0 || unicode.IsControl(r) { return -1 }
return r
}, name)
if name == "" || name == "." || len(name) > 255 {
return "", fmt.Errorf("invalid filename %q", name)
}
return name, nil
}
safe, err := sanitizeFilename(filename)
if err != nil {
return err
}
// also ensure dir is writable (see error 492 validation) Try / catch
path, err := video_gen.TextToSpeech(ctx, cfg, voiceID, transcript, filename)
if err != nil {
if strings.Contains(err.Error(), "create output file") {
var pathErr *os.PathError
if errors.As(err, &pathErr) {
log.Printf("cannot create %s: %v", pathErr.Path, pathErr.Err)
}
return err
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- error reading project configuration file: %v
- error writing project configuration file: %v
- error writing knowledge base file: %v
- error creating company profile file: %v
- error reading company profile file: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/61fe1c6af865ebaa.
Report an issue: GitHub.