SubtitleEdit/subtitleedit · error · InvalidOperationException

Failed to initialize mpv

Error message

Failed to initialize mpv: {player.GetErrorString(err)}

What it means

Thrown inside TextToSpeechViewModel.PlayAudio when a freshly constructed LibMpvDynamicPlayer fails Initialize() during an audio preview in the main Text-to-Speech window. The code disposes any previous preview player, then under _playLock builds a new player, LoadLib(), and Initialize(); a negative return is converted to this InvalidOperationException. As written (lines 2215-2235) the throw is not caught locally — it propagates to the command/preview caller. A subsequent close can null _mpvContext out from under the awaited LoadAudio, which is the separate race noted in the comment.

Solutions

  1. Read the embedded GetErrorString(err) in the exception message for the concrete mpv error.
  2. Confirm libmpv bitness/arch matches the process and install the required C++ runtime.
  3. Re-download the mpv player through SE to replace a corrupt/mismatched libmpv.
  4. Consider guarding LoadLib with CanLoad() and wrapping Initialize in try/catch that resets preview UI state, matching the pattern in ReviewSpeechViewModel.

Example fix

// before
player.LoadLib();
var err = player.Initialize();
if (err < 0) throw new InvalidOperationException($"Failed to initialize mpv: {player.GetErrorString(err)}");

// after - validate load and contain init failures so preview does not strand
player.LoadLib();
if (!player.CanLoad())
    throw new InvalidOperationException("libmpv could not be loaded for audio preview.");
int err;
try { err = player.Initialize(); }
catch (Exception ex) { throw new InvalidOperationException("mpv init threw: " + ex.Message, ex); }
if (err < 0) throw new InvalidOperationException($"Failed to initialize mpv: {player.GetErrorString(err)}");
Defensive patterns

Strategy: try-catch

Validate before calling

player = new LibMpvDynamicPlayer();
if (!player.CanLoad()) throw new InvalidOperationException("libmpv could not be loaded for preview.");

Try / catch

try
{
    lock (_playLock)
    {
        player = new LibMpvDynamicPlayer();
        player.LoadLib();
        var err = player.Initialize();
        if (err < 0) throw new InvalidOperationException($"Failed to initialize mpv: {player.GetErrorString(err)}");
        _mpvContext = player;
    }
    await player.LoadAudio(fileName);
}
catch (Exception ex)
{
    SeLogger.Error(ex, "TTS preview playback failed");
    // reset preview UI state and re-enable controls
}

Prevention

When it happens

Trigger: libmpv present but mpv_initialize returns negative: wrong arch/bitness of libmpv, missing VC++ runtime, corrupt libmpv, or an initialization option rejected by the core.

Common situations: User swapped libmpv for a mismatched build; missing runtime after a fresh Windows install; antivirus damaged libmpv; SE upgraded to a libmpv needing a newer runtime.

Related errors


AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13). Data as JSON: /api/errors/34201f2f568a6d93. Report an issue: GitHub.

Appendix: source

Thrown at src/ui/Features/Video/TextToSpeech/TextToSpeechViewModel.cs:2227

            {
                SeLogger.Error(ex, "TTS window: disposing the audio preview player failed");
            }
        });
    }

    private async Task PlayAudio(string fileName)
    {
        DisposePreviewPlayer();

        LibMpvDynamicPlayer player;
        lock (_playLock)
        {
            player = new LibMpvDynamicPlayer();
            player.LoadLib(); // core not initialized"
            var err = player.Initialize();
            if (err < 0)
            {
                throw new InvalidOperationException($"Failed to initialize mpv: {player.GetErrorString(err)}");
            }

            _mpvContext = player;
        }

        // Through the local: a close running now can null the field out from under us.
        await player.LoadAudio(fileName);
    }

    private async Task<bool> IsEngineInstalled(ITtsEngine engine)
    {
        return await TtsEngineInstaller.EnsureEngineInstalled(engine, Window, _windowService, SelectedRegion, SelectedModel, ApiKey, KeyFile, () => RefreshVoices(engine));
    }

    private async Task MergeAndAddToVideo(TtsStepResult[] fixSpeedResult)
    {
        // Merge audio paragraphs
        var mergedAudioFileName = await MergeAudioParagraphs(fixSpeedResult, _cancellationToken);

View on GitHub (pinned to 17a9f07487)