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

  1. Read the wrapped PathError: it prints the failing path and errno (e.g. 'mkdir /var/media: permission denied') — fix that exact path first
  2. Verify writability as the same user the bot runs as: mkdir -p <dir> && test -w <dir> && touch <dir>/probe
  3. Point the media temp dir setting at a guaranteed-writable directory (e.g. os.TempDir() default or a dedicated writable volume) and retry
  4. If containerized, mount a writable volume/emptyDir at the configured path instead of a read-only one
  5. 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

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


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/7eafbf0e4da8c62c. Report an issue: GitHub.