sipeed/picoclaw · error

failed to create temp file: %w

Error message

failed to create temp file: %w

What it means

Returned when os.CreateTemp(media.TempDir(), "tts-*"+fileExt) fails at pkg/audio/tts/tts.go:137, immediately after the temp dir was created. The pattern is fixed, so failure is environmental: the directory disappeared or is not writable by this process, the process exhausted file descriptors (EMFILE), or the filesystem is full. The deferred cleanup is registered after this point, so no orphan file exists yet.

Source

Thrown at pkg/audio/tts/tts.go:137

		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)
	}

	removeTemp := true
	defer func() {
		if removeTemp {
			_ = os.Remove(file.Name())
		}
	}()

	_, err = io.Copy(file, stream)
	if err != nil {
		_ = file.Close()
		return "", fmt.Errorf("failed to write tts audio: %w", err)
	}

	err = file.Close()
	if err != nil {
		return "", fmt.Errorf("failed to close tts audio file: %w", err)

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Inspect the wrapped errno: EMFILE -> raise ulimit -n / fix fd leak; EACCES -> fix dir ownership (chown to service user); ENOSPC -> free disk
  2. Confirm the directory still exists and is owned by the service user right before retrying: ls -ld <media.TempDir()>
  3. Disable or exclude the media temp dir from aggressive tmp cleaners, or move it out of /tmp
  4. Free disk space (df -h) and retry the TTS request

Example fix

# before: service hits EMFILE during parallel TTS
ulimit -n 1024

# after: allow headroom for concurrent CreateTemp calls
ulimit -n 65536  # or set LimitNOFILE=65536 in the systemd unit
Defensive patterns

Strategy: validation

Validate before calling

// before calling TTS, confirm the dir is usable and fd headroom exists
func canCreateTemp() error {
    f, err := os.CreateTemp(media.TempDir(), "probe-*")
    if err != nil {
        if pe, ok := err.(*fs.PathError); ok && pe.Err == syscall.EMFILE {
            return fmt.Errorf("fd limit exhausted: %w", err)
        }
        return err
    }
    _ = f.Close()
    return os.Remove(f.Name())
}

Try / catch

if _, err := os.CreateTemp(media.TempDir(), "tts-*"+ext); err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) {
        switch {
        case errors.Is(pe.Err, syscall.EMFILE): fixFdLimit()
        case errors.Is(pe.Err, os.ErrPermission): fixDirOwnership()
        case errors.Is(pe.Err, syscall.ENOSPC): freeDisk()
        }
    }
}

Prevention

When it happens

Trigger: A concurrent tmp-reaper (systemd-tmpfiles, cron cleanup) deleting the just-created directory between MkdirAll and CreateTemp; EMFILE from ulimit -n exhaustion with many parallel TTS calls; EACCES because the 0700 dir was created by a different uid in an earlier run; ENOSPC on a full disk.

Common situations: Long-running bot with fd leaks hitting 'too many open files'; shared /tmp cleaned by tmpwatch while a request is in flight; running the service under a different user than the one that owns the media temp dir; disk full from accumulated TTS audio.

Related errors


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