sipeed/picoclaw · error

failed to open audio file: %w

Error message

failed to open audio file: %w

What it means

Thrown by ElevenLabsTranscriber.Transcribe when os.Open(audioFilePath) fails, before any multipart body is built. The error wraps a *os.PathError whose Op is 'open'. No network call is attempted; the ElevenLabs API key is irrelevant to this failure.

Source

Thrown at pkg/audio/asr/elevenlabs_transcriber.go:53

	}

	return &ElevenLabsTranscriber{
		apiKey:  apiKey,
		apiBase: apiBase,
		modelID: modelID,
		httpClient: &http.Client{
			Timeout: 120 * time.Second,
		},
	}
}

func (t *ElevenLabsTranscriber) Transcribe(ctx context.Context, audioFilePath string) (*TranscriptionResponse, error) {
	logger.InfoCF("voice", "Starting ElevenLabs transcription", map[string]any{"audio_file": audioFilePath})

	audioFile, err := os.Open(audioFilePath)
	if err != nil {
		logger.ErrorCF("voice", "Failed to open audio file", map[string]any{"path": audioFilePath, "error": err})
		return nil, fmt.Errorf("failed to open audio file: %w", err)
	}
	defer audioFile.Close()

	fileInfo, err := audioFile.Stat()
	if err != nil {
		logger.ErrorCF("voice", "Failed to get file info", map[string]any{"path": audioFilePath, "error": err})
		return nil, fmt.Errorf("failed to get file info: %w", err)
	}

	logger.DebugCF("voice", "Audio file details", map[string]any{
		"size_bytes": fileInfo.Size(),
		"file_name":  filepath.Base(audioFilePath),
	})

	var requestBody bytes.Buffer
	writer := multipart.NewWriter(&requestBody)

	part, err := writer.CreateFormFile("file", filepath.Base(audioFilePath))

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Verify the path with ls/stat in the same environment the transcriber runs in.
  2. Fix permissions (chmod/chown) if EACCES; check fd usage (lsof | wc -l) if EMFILE.
  3. Ensure the caller keeps the recorded file alive until Transcribe returns.
  4. Pre-validate with os.Stat before invoking Transcribe.

Example fix

// before
resp, err := elevenLabs.Transcribe(ctx, tmpPath)

// after
if _, err := os.Stat(tmpPath); err != nil {
    return fmt.Errorf("cannot transcribe, audio file unreadable: %w", err)
}
resp, err := elevenLabs.Transcribe(ctx, tmpPath)
Defensive patterns

Strategy: validation

Validate before calling

if fi, err := os.Stat(audioFilePath); err != nil {
    return fmt.Errorf("audio missing: %w", err)
} else if fi.IsDir() || fi.Size() == 0 {
    return fmt.Errorf("audio invalid (dir or empty): %s", audioFilePath)
}

Type guard

func isNotFound(err error) bool { return errors.Is(err, fs.ErrNotExist) }

Try / catch

if _, err := el.Transcribe(ctx, path); err != nil {
    if isNotFound(err) {
        log.Printf("recording vanished: %s", path) // pipeline bug, no retry
        return
    }
    return err
}

Prevention

When it happens

Trigger: os.Open fails with ENOENT (file missing), EACCES (no read permission), EISDIR (path is a directory), or EMFILE (process out of file descriptors after leaking handles).

Common situations: Temp audio file already cleaned by a cleanup routine; file recorded in a container but transcription runs on the host (or vice versa); fd leak in a long-running agent after many recordings without Close.

Related errors


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