Skip to content

Commit c7c8129

Browse files
dblockCopilot
andcommitted
Fix SystemStackError caused by infinite recursion in _handle_result
When an error occurred during result handling (e.g., a logging error), the rescue block in _handle_result would recursively call itself, leading to infinite recursion and SystemStackError. This fix prevents recursion by directly invoking the error callbacks instead of calling _handle_result again. The logger.error call is also wrapped with rescue nil to prevent logging errors from propagating. Fixes #12 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent e665f74 commit c7c8129

File tree

4 files changed

+67
-11
lines changed

4 files changed

+67
-11
lines changed

.rubocop_todo.yml

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
# This configuration was generated by
22
# `rubocop --auto-gen-config`
3-
# on 2025-07-05 14:01:58 UTC using RuboCop version 1.77.0.
3+
# on 2026-01-29 18:49:42 UTC using RuboCop version 1.84.0.
44
# The point is for the user to remove these configuration records
55
# one by one as the offenses are removed from the code base.
66
# Note that changes in the inspected code, or installation of new
77
# versions of RuboCop, may require this file to be generated again.
88

99
# Offense count: 1
10-
# Configuration parameters: EnforcedStyle, AllowedGems, Include.
10+
# Configuration parameters: EnforcedStyle, AllowedGems.
1111
# SupportedStyles: Gemfile, gems.rb, gemspec
12-
# Include: **/*.gemspec, **/Gemfile, **/gems.rb
1312
Gemspec/DevelopmentDependencies:
1413
Exclude:
1514
- 'ruby-link-checker.gemspec'
@@ -36,9 +35,20 @@ Layout/EmptyLineAfterMagicComment:
3635
- 'spec/ruby-link-checker/checker_spec.rb'
3736
- 'spec/ruby-link-checker/config_spec.rb'
3837

38+
# Offense count: 4
39+
# Configuration parameters: EnforcedStyle.
40+
# SupportedStyles: native, lf, crlf
41+
Layout/EndOfLine:
42+
Exclude:
43+
- 'lib/ruby-link-checker/logger.rb'
44+
- 'spec/ruby-link-checker/checker_spec.rb'
45+
- 'spec/ruby-link-checker/config_spec.rb'
46+
- 'spec/spec_helper.rb'
47+
3948
# Offense count: 3
4049
# This cop supports safe autocorrection (--autocorrect).
41-
# Configuration parameters: Width, AllowedPatterns.
50+
# Configuration parameters: Width, EnforcedStyleAlignWith, AllowedPatterns.
51+
# SupportedStylesAlignWith: start_of_line, relative_to_receiver
4252
Layout/IndentationWidth:
4353
Exclude:
4454
- 'spec/ruby-link-checker/checker_spec.rb'
@@ -141,7 +151,7 @@ RSpec/EmptyLineAfterExample:
141151
Exclude:
142152
- 'spec/ruby-link-checker/config_spec.rb'
143153

144-
# Offense count: 13
154+
# Offense count: 14
145155
# Configuration parameters: CountAsOne.
146156
RSpec/ExampleLength:
147157
Max: 9
@@ -191,16 +201,17 @@ RSpec/NamedSubject:
191201
RSpec/NestedGroups:
192202
Max: 6
193203

194-
# Offense count: 6
195-
# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata.
196-
# Include: **/*_spec.rb
204+
# Offense count: 7
205+
# Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector.
206+
# SupportedInflectors: default, active_support
197207
RSpec/SpecFilePathFormat:
198208
Exclude:
199209
- '**/spec/routing/**/*'
200210
- 'spec/ruby-link-checker/callbacks_spec.rb'
201211
- 'spec/ruby-link-checker/checker_spec.rb'
202212
- 'spec/ruby-link-checker/config_spec.rb'
203213
- 'spec/ruby-link-checker/net/http/checker_spec.rb'
214+
- 'spec/ruby-link-checker/tasks_spec.rb'
204215
- 'spec/ruby-link-checker/typhoeus/hydra/checker_spec.rb'
205216
- 'spec/ruby-link-checker/version_spec.rb'
206217

@@ -307,6 +318,12 @@ Style/NumericPredicate:
307318
- 'spec/**/*'
308319
- 'lib/ruby-link-checker/tasks.rb'
309320

321+
# Offense count: 1
322+
# This cop supports safe autocorrection (--autocorrect).
323+
Style/RescueModifier:
324+
Exclude:
325+
- 'lib/ruby-link-checker/tasks.rb'
326+
310327
# Offense count: 1
311328
# This cop supports unsafe autocorrection (--autocorrect-all).
312329
# Configuration parameters: Mode.
@@ -344,7 +361,7 @@ Style/SuperWithArgsParentheses:
344361

345362
# Offense count: 1
346363
# This cop supports safe autocorrection (--autocorrect).
347-
# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
364+
# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
348365
# URISchemes: http, https
349366
Layout/LineLength:
350367
Max: 123

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
### 0.3.1 (Next)
22

3+
* [#13](https://github.com/dblock/ruby-link-checker/pull/13): Fix `SystemStackError` caused by infinite recursion in `_handle_result` - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/Copilot).
34
* [#14](https://github.com/dblock/ruby-link-checker/pull/14): Migrate Danger to use danger-pr-comment workflow - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/Copilot).
45
* [#14](https://github.com/dblock/ruby-link-checker/pull/14): Removed Code Climate - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/Copilot).
56
* [#15](https://github.com/dblock/ruby-link-checker/pull/15): Added Codecov - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/Copilot).

lib/ruby-link-checker/tasks.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,10 @@ def _handle_result(result)
103103
execute!
104104
end
105105
rescue StandardError => e
106-
logger.error("#{self}##{__method__}") { e }
107-
_handle_result ResultError.new(result.uri, result.method, result.result_uri, e, options)
106+
logger.error("#{self}##{__method__}") { e } rescue nil
107+
error_result = ResultError.new(result.uri, result.method, result.result_uri, e, options)
108+
result! error_result
109+
error! error_result
108110
end
109111
end
110112
end
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe LinkChecker::Tasks do
6+
describe '#_handle_result' do
7+
# Reproduces issue #12: SystemStackError when logging causes repeated errors
8+
# https://github.com/dblock/ruby-link-checker/issues/12
9+
context 'when logger.info raises an error repeatedly' do
10+
let(:checker) { LinkChecker::Net::HTTP::Checker.new(methods: ['GET']) }
11+
let(:task_klass) { LinkChecker::Net::HTTP::Task }
12+
let(:url) { 'https://www.example.org' }
13+
14+
before do
15+
stub_request(:get, url).to_return(status: 200, body: '', headers: {})
16+
end
17+
18+
it 'does not cause infinite recursion leading to SystemStackError' do
19+
tasks = described_class.new(checker, task_klass, url, ['GET'])
20+
21+
# Stub logger to always raise an error on info, simulating the scenario
22+
# from issue #12 where logging causes a stack overflow due to the
23+
# rescue block in _handle_result recursively calling itself
24+
allow(tasks).to receive(:logger).and_return(
25+
instance_double(LinkChecker::Logger).tap do |logger|
26+
allow(logger).to receive(:info).and_raise(StandardError, 'stack level too deep')
27+
allow(logger).to receive(:error)
28+
end
29+
)
30+
31+
# Should not raise SystemStackError - the error should be handled gracefully
32+
expect { tasks.execute! }.not_to raise_error
33+
end
34+
end
35+
end
36+
end

0 commit comments

Comments
 (0)