sipeed/picoclaw · error
failed to create media temp dir: %w
Error message
failed to create media temp dir: %w
What it means
Thrown by the TTS tool path (pkg/audio/tts/tts.go:119) when os.MkdirAll(media.TempDir(), 0o700) fails before a synthesized audio stream is spooled to disk. The wrapped error is a *fs.PathError naming the exact directory and the syscall errno, so the root cause is always filesystem-level. Synthesis itself already succeeded; only scratch-space preparation failed, and no temp file was created.
Source
Thrown at pkg/audio/tts/tts.go:119
if store == nil {
return "", fmt.Errorf("media store not configured")
}
if channel == "" || chatID == "" {
return "", fmt.Errorf("no target channel/chat available")
}
if strings.TrimSpace(text) == "" {
return "", fmt.Errorf("text is required")
}
stream, err := provider.Synthesize(ctx, text)
if err != nil {
return "", fmt.Errorf("tts synthesize failed: %w", err)
}
defer stream.Close()
err = os.MkdirAll(media.TempDir(), 0o700)
if err != nil {
return "", fmt.Errorf("failed to create media temp dir: %w", err)
}
fileExt := ".ogg"
contentType := "audio/ogg"
if provider.Name() == "mimo-tts" {
fileExt = ".mp3"
contentType = "audio/mpeg"
}
if metaProvider, ok := stream.(ttsAudioMetaProvider); ok {
if ext, ct := metaProvider.AudioFileMeta(); ext != "" && ct != "" {
fileExt = ext
contentType = ct
}
}
file, err := os.CreateTemp(media.TempDir(), "tts-*"+fileExt)
if err != nil {
return "", fmt.Errorf("failed to create temp file: %w", err)View on GitHub (pinned to 49183d7e8d)
Solutions
- Read the wrapped PathError: it prints the failing path and errno (e.g. 'mkdir /var/media: permission denied') — fix that exact path first
- Verify writability as the same user the bot runs as: mkdir -p <dir> && test -w <dir> && touch <dir>/probe
- Point the media temp dir setting at a guaranteed-writable directory (e.g. os.TempDir() default or a dedicated writable volume) and retry
- If containerized, mount a writable volume/emptyDir at the configured path instead of a read-only one
- Check disk space and inodes: df -h <dir> && df -i <dir>
Example fix
// before: media temp dir points into a read-only mount
// config: temp_dir = "/var/picoclaw/media" (read-only volume)
// after: point at a writable scratch location (or unset to use os.TempDir)
// config: temp_dir = "/var/tmp/picoclaw-media"
// verify before first use:
if err := os.MkdirAll(media.TempDir(), 0o700); err != nil {
log.Printf("tts scratch dir unusable: %v", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// preflight before first TTS call / at service startup
func checkTTSScratchDir() error {
dir := media.TempDir()
if err := os.MkdirAll(dir, 0o700); err != nil {
return fmt.Errorf("temp dir %s unusable: %w", dir, err)
}
probe := filepath.Join(dir, ".probe")
if err := os.WriteFile(probe, nil, 0o600); err != nil {
return fmt.Errorf("temp dir %s not writable: %w", dir, err)
}
return os.Remove(probe)
} Try / catch
if ref, err := tts.SynthesizeToStore(ctx, text, ch, chatID, name); err != nil {
var pathErr *fs.PathError
if errors.As(err, &pathErr) && strings.HasPrefix(pathErr.Path, media.TempDir()) {
// filesystem-level: surface config problem, do not retry
log.Printf("media temp dir problem at %s: %v", pathErr.Path, pathErr.Err)
}
return err
} Prevention
- Validate media.TempDir() writability at startup with a mkdir+touch probe
- Keep the media temp dir on local writable storage, out of /tmp cleaner scope
- Run the service under a user that owns the media temp dir
- Monitor disk space on the volume hosting the temp dir
When it happens
Trigger: Calling the send_tts tool (SynthesizeToStore) when media.TempDir() points to: a path occupied by a regular file (ENOTDIR), a directory owned by another user or without write permission (EACCES), a read-only mount/container rootfs (EROFS), a missing parent that cannot be created (ENOENT), or a full disk (ENOSPC).
Common situations: media/temp_dir misconfigured in picoclaw config to an unwritable location; running in Docker with a read-only volume; HOME unset so the temp path resolves oddly; SELinux/AppArmor denying mkdir; sticky-bit /tmp dir created by a different uid with 0700.
Related errors
- failed to create temp file: %w
- create output dir: %w
- failed to save config: %w
- ✗ failed to create skills directory: %w
- failed to close tts audio file: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/7eafbf0e4da8c62c.
Report an issue: GitHub.