Comfy-Org/ComfyUI · error · ValueError

AudioJoin: Both input audios must be mono.

Error message

AudioJoin: Both input audios must be mono.

What it means

JoinAudioChannels stacks two mono signals into stereo by slicing channel dim 1; it hard-requires both inputs to have exactly 1 channel. Either input being stereo or multichannel raises before sample-rate matching runs.

Source

Thrown at comfy_extras/nodes_audio.py:550

                IO.Audio.Output(display_name="audio"),
            ],
        )

    @classmethod
    def execute(cls, audio_left, audio_right) -> IO.NodeOutput:
        if audio_left is None and audio_right is None:
            return IO.NodeOutput(None)
        if audio_left is None:
            return IO.NodeOutput(audio_right)
        if audio_right is None:
            return IO.NodeOutput(audio_left)
        waveform_left = audio_left["waveform"]
        sample_rate_left = audio_left["sample_rate"]
        waveform_right = audio_right["waveform"]
        sample_rate_right = audio_right["sample_rate"]

        if waveform_left.shape[1] != 1 or waveform_right.shape[1] != 1:
            raise ValueError("AudioJoin: Both input audios must be mono.")

        # Handle different sample rates by resampling to the higher rate
        waveform_left, waveform_right, output_sample_rate = match_audio_sample_rates(
            waveform_left, sample_rate_left, waveform_right, sample_rate_right
        )

        # Handle different lengths by trimming to the shorter length
        length_left = waveform_left.shape[-1]
        length_right = waveform_right.shape[-1]

        if length_left != length_right:
            min_length = min(length_left, length_right)
            if length_left > min_length:
                logging.info(f"JoinAudioChannels: Trimming left channel from {length_left} to {min_length} samples.")
                waveform_left = waveform_left[..., :min_length]
            if length_right > min_length:
                logging.info(f"JoinAudioChannels: Trimming right channel from {length_right} to {min_length} samples.")
                waveform_right = waveform_right[..., :min_length]

View on GitHub (pinned to 1c6d8d45b3)

Solutions

  1. Downmix or select a single channel per input first: wav[:, :1, :] or mean over dim 1
  2. Use SplitAudioChannels to extract a mono channel from a stereo input before joining
  3. Re-export sources as mono where a mono-to-stereo placement is intended
  4. Assert both shapes are (B, 1, N) upstream so the failure is caught at the boundary

Example fix

// before
stereo = JoinAudioChannels(music_stereo, voice_mono)  # raises

// after
music_mono = {'waveform': music_stereo['waveform'][:, :1, :], 'sample_rate': music_stereo['sample_rate']}
stereo = JoinAudioChannels(music_mono, voice_mono)
Defensive patterns

Strategy: type-guard

Validate before calling

def to_mono(audio):
    wf = audio['waveform']
    if wf.shape[1] > 1:
        wf = wf[:, :1, :]  # or wf.mean(dim=1, keepdim=True)
    return {'waveform': wf, 'sample_rate': audio['sample_rate']}

Type guard

def is_mono(audio) -> bool:
    return audio is not None and audio['waveform'].shape[1] == 1

Prevention

When it happens

Trigger: Passing a stereo file's audio as audio_left/audio_right; passing 4/6-channel surround audio; upstream nodes that output batched multi-channel tensors where dim 1 > 1.

Common situations: Mixing audio from heterogeneous sources (one mono mic, one stereo music bed) into a stereo join; re-using a join template after the source export changed to stereo.

Related errors


AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14). Data as JSON: /api/errors/9465979c19bac4b6. Report an issue: GitHub.