NoMethodError in Rapid Diffs when rendering Jupyter notebooks with nil diff

Summary

A NoMethodError occurs when rendering Jupyter notebook (.ipynb) files in the rapid diffs view when the notebook diff parsing fails.

Sentry: https://new-sentry.gitlab.net/organizations/gitlab/issues/3179477

Error

NoMethodError: undefined method `deleted_file?' for nil (NoMethodError)
  from lib/gitlab/diff/file.rb:11:in `deleted_file?'
  from lib/gitlab/diff/file.rb:130:in `new_content_sha'
  from lib/gitlab/diff/file.rb:168:in `content_sha'
  from app/components/rapid_diffs/diff_file_component.rb:22:in `file_data'

Root Cause

The Gitlab::Diff::Rendered::Notebook::DiffFile class extends Gitlab::Diff::File and overrides the diff method with a memoized call to transformed_diff. However, transformed_diff returns nil when notebook_diff is nil, which happens in the following scenarios:

  1. Notebook parsing times out (exceeds RENDERED_TIMEOUT_BACKGROUND of 10 seconds)
  2. Invalid notebook format (IpynbDiff::InvalidNotebookError)
  3. Truncated blob (either old or new blob is truncated)

The parent class Gitlab::Diff::File delegates deleted_file? to @diff:

# lib/gitlab/diff/file.rb:11
delegate :new_file?, :deleted_file?, :renamed_file?, ...
  to: :diff, prefix: false

When diff is nil, calling any delegated method raises NoMethodError.

Call Chain

  1. DiffFileComponent#initialize sets @diff_file = diff_file.rendered || diff_file
  2. For .ipynb files, rendered returns a Notebook::DiffFile instance
  3. DiffFileComponent#file_data calls @diff_file.content_sha
  4. content_sha calls new_content_sha || old_content_sha
  5. new_content_sha calls deleted_file? which is delegated to diff
  6. diff is nil → NoMethodError

Proposed Solutions

  1. Override delegated methods in Notebook::DiffFile to handle the nil diff case gracefully
  2. Add guard in DiffFileComponent to check has_renderable? before accessing content_sha
  3. Return a null object from transformed_diff instead of nil

Affected Files

  • lib/gitlab/diff/rendered/notebook/diff_file.rb
  • lib/gitlab/diff/file.rb
  • app/components/rapid_diffs/diff_file_component.rb
Edited Feb 02, 2026 by 🤖 GitLab Bot 🤖
Assignee Loading
Time tracking Loading