chenhg5/cc-connect · error

pico2wave: read output file: %w

Error message

pico2wave: read output file: %w

What it means

After pico2wave exits successfully, PicoTTS.Synthesize reads back the WAV file it was told to write (os.ReadFile on the temp path); this error wraps a failure to read that file. Since CreateTemp succeeded moments earlier, this usually means pico2wave never actually wrote the file (exit 0 without producing output), the file was removed in between, or namespace isolation (e.g. systemd PrivateTmp) gave the child process a different view of the temp directory.

Source

Thrown at core/tts.go:646

	// --lang: language code (zh-CN for Chinese, en-US for English)
	// --wave: output WAV file path
	args := []string{
		"--lang=" + voice,
		"--wave=" + tmpPath,
		text,
	}

	// Execute pico2wave command
	cmd := exec.CommandContext(ctx, p.Path, args...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return nil, "", fmt.Errorf("pico2wave: voice=%s text=%q: %w, output: %s", voice, text, err, string(output))
	}

	// Read the generated WAV file
	audioData, err := os.ReadFile(tmpPath)
	if err != nil {
		return nil, "", fmt.Errorf("pico2wave: read output file: %w", err)
	}

	if len(audioData) == 0 {
		return nil, "", fmt.Errorf("pico2wave: produced empty audio file")
	}

	return audioData, "wav", nil
}

// ──────────────────────────────────────────────────────────────
// EdgeTTS — Microsoft Edge TTS (free, high quality, requires network)
// ──────────────────────────────────────────────────────────────

// EdgeTTS implements TextToSpeech using Microsoft Edge's free TTS API.
// This uses the edge-tts CLI command under the hood.
type EdgeTTS struct {
	Path  string // path to edge-tts executable (empty = "edge-tts")
	Voice string // default voice (e.g. "zh-CN-XiaoxiaoNeural")

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Check file existence right before reading (os.Stat) and report the temp path plus pico2wave's combined output in the error for diagnosis.
  2. Guard empty text before invoking pico2wave: return an error when strings.TrimSpace(text) == "".
  3. Re-run the exact command manually to confirm pico2wave creates the file: pico2wave --lang=en-US --wave=/tmp/t.wav "hello".
  4. Check for PrivateTmp/namespace isolation (systemd, containers) making the child's tmp differ; use a shared writable directory.
  5. Include tmpPath in the error message to distinguish 'file missing' from 'permission/read failure'.

Example fix

// before
audioData, err := os.ReadFile(tmpPath)
if err != nil {
	return nil, "", fmt.Errorf("pico2wave: read output file: %w", err)
}
// after
if _, statErr := os.Stat(tmpPath); statErr != nil {
	return nil, "", fmt.Errorf("pico2wave: wrote no output file %s (exit 0, output: %s): %w", tmpPath, string(output), statErr)
}
audioData, err := os.ReadFile(tmpPath)
if err != nil {
	return nil, "", fmt.Errorf("pico2wave: read output file %s: %w", tmpPath, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: guard empty text — the main cause of silent no-output runs
if strings.TrimSpace(text) == "" {
	return errors.New("pico2wave: empty text")
}

Try / catch

audio, format, err := pico.Synthesize(ctx, text, opts)
if err != nil {
	if strings.Contains(err.Error(), "read output file") {
		// pico2wave exited 0 but wrote nothing — treat as engine failure;
		// check PrivateTmp/tmp isolation, then retry with another engine
		return espeak.Synthesize(ctx, text, opts)
	}
	return err
}

Prevention

When it happens

Trigger: Calling Synthesize via PicoTTS when the pico2wave process returns exit code 0 but does not create/write --wave=<tmpPath> (e.g. empty or no-op text); the temp file is deleted between creation and read; the deferred os.Remove of an overlapping call removes it; or the child runs in a private tmp namespace.

Common situations: pico2wave builds that report success while failing to synthesize (empty text producing no output); systemd PrivateTmp or container namespace isolation separating the child's /tmp from the parent's; aggressive /tmp cleaners; text treated as a no-op by the engine.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/73d5209fe2e5baee. Report an issue: GitHub.