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:
-
Notebook parsing times out (exceeds
RENDERED_TIMEOUT_BACKGROUNDof 10 seconds) -
Invalid notebook format (
IpynbDiff::InvalidNotebookError) - 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
-
DiffFileComponent#initializesets@diff_file = diff_file.rendered || diff_file - For
.ipynbfiles,renderedreturns aNotebook::DiffFileinstance -
DiffFileComponent#file_datacalls@diff_file.content_sha -
content_shacallsnew_content_sha || old_content_sha -
new_content_shacallsdeleted_file?which is delegated todiff -
diffisnil→NoMethodError
Proposed Solutions
-
Override delegated methods in
Notebook::DiffFileto handle thenildiff case gracefully -
Add guard in
DiffFileComponentto checkhas_renderable?before accessingcontent_sha -
Return a null object from
transformed_diffinstead ofnil
Affected Files
lib/gitlab/diff/rendered/notebook/diff_file.rblib/gitlab/diff/file.rbapp/components/rapid_diffs/diff_file_component.rb
Edited by 🤖 GitLab Bot 🤖