Skip to content

Handle invalid audio waveform values in SaveVideo without failing prompt#13193

Draft
iqdoctor wants to merge 2 commits intoComfy-Org:masterfrom
iqdoctor:fix/audio-nan-savevideo-20260327
Draft

Handle invalid audio waveform values in SaveVideo without failing prompt#13193
iqdoctor wants to merge 2 commits intoComfy-Org:masterfrom
iqdoctor:fix/audio-nan-savevideo-20260327

Conversation

@iqdoctor
Copy link
Copy Markdown

Fixes #13192

What

Harden VideoFromComponents.save_to() audio path to prevent full prompt failure when audio tensor contains malformed values.

Changes in comfy_api/latest/_input_impl/video_types.py:

  • sanitize waveform before encode: torch.nan_to_num(...).clamp(-1.0, 1.0)
  • re-check audio_np with np.isfinite and np.nan_to_num
  • add small mux helper handling packet/list outputs from encode
  • wrap audio encode/mux in try/except and continue with video-only output on failure

Why

Current behavior can throw:

av.error.ArgumentError: Invalid argument: 'avcodec_send_frame()' returned 22

which aborts the whole prompt during save. This is especially visible in long video+audio workflows.

Validation

  • Local syntax check: python3 -m py_compile comfy_api/latest/_input_impl/video_types.py
  • Runtime validation on Linux host with ComfyUI at 225c52f6:
    • generated synthetic NaN/Inf audio tensor
    • VideoFromComponents(...).save_to('/tmp/comfy_audio_nan_inf_test.mp4') succeeded
    • output file created and non-zero size

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 462592a359

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +479 to +482
except Exception as exc:
logger.warning(
"Failed to encode audio track, saving video-only output: %s", exc
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid swallowing non-audio-data write failures

The broad except Exception here will also catch output.mux(...) failures (for example, filesystem write errors such as a full disk), then log a warning and return as if save succeeded. That changes prior behavior from explicit failure to silent partial/corrupt output, which can hide real save errors in production workflows. Consider limiting the fallback to known audio-content errors (e.g., frame creation/encode argument errors) and letting I/O/mux failures propagate.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 628af86: narrowed fallback to audio-data/encode exceptions only (av.error.ArgumentError, ValueError, TypeError), moved mux outside fallback so filesystem/I/O write failures propagate as hard failures. Also switched fallback log to explicit ERROR message for operator logs.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

📝 Walkthrough

Walkthrough

Added module-level logging and a helper mux_packets to handle single or iterable encoded packets while skipping None. Audio preprocessing now sanitizes waveform tensors by replacing non-finite values and clamping to [-1.0, 1.0] before conversion to NumPy. Audio encoding and flush are wrapped in try/except to catch av.error.ArgumentError, ValueError, and TypeError; on error the audio track is stopped and processing continues without audio. Encoded and flush packets are muxed via mux_packets.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: handling invalid audio waveform values to prevent prompt failure during video save operations.
Description check ✅ Passed The description is well-related to the changeset, explaining the issue, the solution, and validation approach with specific technical details.
Linked Issues check ✅ Passed All four coding objectives from issue #13192 are fully addressed: waveform sanitization with torch.nan_to_num and clamping, numpy buffer re-checking with np.isfinite and np.nan_to_num, try/except wrapping for audio encode/mux with video-only fallback, and mux helper supporting packet/list outputs.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the audio encoding path in video_types.py and align with the stated objectives; no out-of-scope modifications were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@iqdoctor
Copy link
Copy Markdown
Author

Thanks, valid point.

You are right that the previous broad except Exception could unintentionally hide non-audio write failures (e.g. filesystem/mux errors).

I pushed a follow-up commit to narrow fallback handling:

  • fallback is now limited to audio-data / audio-encode failures only: av.error.ArgumentError, ValueError, TypeError
  • output.mux(...) is no longer inside the fallback catch path, so I/O/mux errors propagate as hard failures (same failure semantics for disk/full/corrupt write paths)
  • caught fallback cases are logged as explicit ERROR with a clear message (Audio encode failed due to invalid audio data...)

Commit: 628af864

Operational context from production (str/vm): this path is currently required for long video+audio runs in LTX-2.3 workflows (checkpoint ltx-2.3-22b-dev-fp8.safetensors), where malformed audio tensors can appear intermittently on durations >5s. We also plan a separate root-cause fix on model/node side and will open a dedicated PR there.

@iqdoctor iqdoctor marked this pull request as draft March 27, 2026 13:48
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@comfy_api/latest/_input_impl/video_types.py`:
- Around line 467-486: The except clause is catching a non-existent
av.error.ArgumentError which will raise AttributeError at runtime; update the
exception handling around the audio encoding block (the try that calls
waveform.float().cpu().contiguous().numpy(), av.AudioFrame.from_ndarray,
audio_stream.encode and then mux_packets) to remove av.error.ArgumentError and
only catch the valid exceptions (ValueError, TypeError) so the logger.error
branch still runs for encoding/validation failures without referencing a
nonexistent symbol.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ab6b83c0-caee-4a13-8c53-fa879b3156f8

📥 Commits

Reviewing files that changed from the base of the PR and between 462592a and 628af86.

📒 Files selected for processing (1)
  • comfy_api/latest/_input_impl/video_types.py

Comment on lines +467 to +486
encoded_audio_packets = None
flush_audio_packets = None
try:
audio_np = waveform.float().cpu().contiguous().numpy()
if not np.isfinite(audio_np).all():
audio_np = np.nan_to_num(audio_np, nan=0.0, posinf=0.0, neginf=0.0)

frame = av.AudioFrame.from_ndarray(audio_np, format='fltp', layout=layout)
frame.sample_rate = audio_sample_rate
frame.pts = 0
encoded_audio_packets = audio_stream.encode(frame)
flush_audio_packets = audio_stream.encode(None)
except (av.error.ArgumentError, ValueError, TypeError) as exc:
logger.error(
"Audio encode failed due to invalid audio data; skipping audio track and saving video-only output: %s",
exc,
)
else:
mux_packets(output, encoded_audio_packets)
mux_packets(output, flush_audio_packets)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

PyAV av.error.ArgumentError exception class

💡 Result:

There is no av.error.ArgumentError exception class in PyAV. Official PyAV documentation across versions (9.0.2, 14.3.0, 15.0.0, 17.0.0) explicitly states that FFmpeg argument errors (EINVAL) are mapped to av.error.ValueError, which inherits from both ValueError and FFmpegError. This allows catching via either except av.FFmpegError or except ValueError. No search results, GitHub issues, or source code snippets mention or define av.error.ArgumentError. Common "Invalid argument" errors (Errno 22, EINVAL) raise ValueError, OSError, or specific subclasses like InvalidDataError depending on context. PyAV error handling translates FFmpeg errors to Python builtin exceptions where possible, shadowing the hierarchy under av.FFmpegError. A complete list of specific exceptions includes BSFNotFoundError, BugError, BufferTooSmallError, etc., but not ArgumentError.

Citations:


Remove non-existent av.error.ArgumentError from exception handling.

The except clause attempts to catch av.error.ArgumentError, which does not exist in PyAV. Official PyAV documentation confirms that FFmpeg argument errors (EINVAL) are mapped to av.error.ValueError, not a separate ArgumentError class. This will cause an AttributeError at runtime.

Replace:

except (av.error.ArgumentError, ValueError, TypeError) as exc:

with:

except (ValueError, TypeError) as exc:

The ValueError already covers FFmpeg argument validation errors via PyAV's error mapping.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@comfy_api/latest/_input_impl/video_types.py` around lines 467 - 486, The
except clause is catching a non-existent av.error.ArgumentError which will raise
AttributeError at runtime; update the exception handling around the audio
encoding block (the try that calls waveform.float().cpu().contiguous().numpy(),
av.AudioFrame.from_ndarray, audio_stream.encode and then mux_packets) to remove
av.error.ArgumentError and only catch the valid exceptions (ValueError,
TypeError) so the logger.error branch still runs for encoding/validation
failures without referencing a nonexistent symbol.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SaveVideo: avoid fatal avcodec_send_frame(22) on invalid audio tensors

1 participant