Skip to main content

Ruby Troubleshooting

Debug Knapsack Pro on your development environment/machine​

To reproduce what Knapsack Pro executed on a specific CI node, check out the same branch and run:

KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC=MY_TOKEN \
KNAPSACK_PRO_CI_NODE_INDEX=MY_INDEX \
KNAPSACK_PRO_CI_NODE_TOTAL=MY_TOTAL \
KNAPSACK_PRO_BRANCH=MY_BRANCH \
KNAPSACK_PRO_COMMIT_HASH=MY_COMMIT \
KNAPSACK_PRO_TEST_QUEUE_ID=MY_TEST_QUEUE_ID \
bundle exec rake "knapsack_pro:queue:rspec[--seed MY_SEED]"

KNAPSACK_PRO_CI_NODE_BUILD_ID must be the same as the CI build you are trying to reproduce (if it helps, take a look at what Knapsack Pro uses as node_build_id for your CI provider).

You can also run the same subset of tests without Knapsack Pro: in the logs, find the command that Knapsack Pro used to invoke the test runner.

You will find multiple commands to reproduce each batch:

[knapsack_pro] To retry the last batch of tests fetched from the API Queue, please run the following command on your machine:
[knapsack_pro] bundle exec rspec --format documentation --format RspecJunitFormatter --out tmp/rspec.xml --default-path spec --seed 24098 "spec/features/dashboard/billing/subscription_error_path_1_spec.rb[1:1:2:1:1:1]"

Or a single command at the end to execute all the batches the CI node executed:

[knapsack_pro] To retry all the tests assigned to this CI node, please run the following command on your machine:
[knapsack_pro] bundle exec rspec --format documentation --format RspecJunitFormatter --out tmp/rspec.xml --default-path spec --seed 24098 "spec/features/dashboard/billing/subscription_error_path_1_spec.rb[1:1:2:1:1:1]" "spec/features/dashboard/builds/build_distribution_for_build_spec.rb[1:1:1:10:2:1]" "spec/features/dashboard/builds/build_distribution_for_build_spec.rb[1:1:2:1]" "spec/features/dashboard/admin_statistics_spec.rb[1:6:1:1]" "spec/features/dashboard/identity_providers_spec.rb[1:6:1]" "spec/features/dashboard/onboarding_spec.rb[1:2:1:1:1]"

Knapsack Pro hangs​

Some users reported frozen CI nodes with:

  • Test runners producing too much output: reduce it with KNAPSACK_PRO_LOG_LEVEL=warn

  • Test runners producing no output: make sure to use a formatter (like RSpec's --format progress)

  • Timecop (e.g., Travis): ensure Timecop.return is executed after all examples

    # spec/spec_helper.rb

    RSpec.configure do |c|
    c.after(:all) do
    Timecop.return
    end
    end
  • Chrome 83+ prevents downloads in sandboxed iframes: add an allow-downloads keyword in the sandbox attribute list

  • Ensure you use the latest version of the simplecov gem that fixes the bug.

  • Too much memory consumption, leading to a timeout on the CI:

    • A heap size too big in Elasticsearch could lead to saturated memory. Add ES_JAVA_OPTS: "-Xms1024m -Xmx1024m" to force the JVM to use 1GB for the heap size.
  • Recursive loop in the code base. Check if you have hidden loops that could happen when an exception happens. For example, investigate nested rescue blocks. You can use the following backtrace debugging suggestions to find it.

  • Cloudflare JS challenge blocking external requests during tests if your CI runs from outside the allowed regions.

Why does the CI node hang? (backtrace debugging)​

Knapsack Pro prints the Ruby threads' backtraces when killed with TERM:

Tests output
================================================================================
Start logging 2 detected threads.
Use the following backtrace(s) to find the line of code that got stuck if the CI node hung and terminated your tests.
How to read the backtrace: https://knapsackpro.com/perma/ruby/backtrace-debugging

Running specs in the main thread:
knapsack_pro-ruby/spec_integration/b_spec.rb:7:in `kill'
knapsack_pro-ruby/spec_integration/b_spec.rb:7:in `block (3 levels) in <top (required)>'


Non-main thread inspect: #<Thread:0x000000011e18f6e0 spec_integration/a_spec.rb:5 sleep>
Running specs in non-main thread:
knapsack_pro-ruby/spec_integration/a_spec.rb:6:in `sleep'
knapsack_pro-ruby/spec_integration/a_spec.rb:6:in `block (3 levels) in <top (required)>'


Main thread backtrace:
knapsack_pro-ruby/lib/knapsack_pro/runners/queue/base_runner.rb:83:in `backtrace'
knapsack_pro-ruby/lib/knapsack_pro/runners/queue/base_runner.rb:83:in `block in log_threads'
knapsack_pro-ruby/lib/knapsack_pro/runners/queue/base_runner.rb:75:in `each'
knapsack_pro-ruby/lib/knapsack_pro/runners/queue/base_runner.rb:75:in `log_threads'
knapsack_pro-ruby/lib/knapsack_pro/runners/queue/base_runner.rb:62:in `block (2 levels) in trap_signals'
knapsack_pro-ruby/spec_integration/b_spec.rb:7:in `kill'
knapsack_pro-ruby/spec_integration/b_spec.rb:7:in `block (3 levels) in <top (required)>'
.rvm/gems/ruby-3.3.4/gems/rspec-core-3.13.0/lib/rspec/core/example.rb:263:in `instance_exec'
...


Non-main thread inspect: #<Thread:0x000000011e18f6e0 spec_integration/a_spec.rb:5 sleep>
Non-main thread backtrace:
knapsack_pro-ruby/spec_integration/a_spec.rb:6:in `sleep'
knapsack_pro-ruby/spec_integration/a_spec.rb:6:in `block (3 levels) in <top (required)>'


End logging threads.
================================================================================

The above-highlighted lines of code got stuck. See them in the following spec files.

The main thread was killed due to the following b_spec.rb:7 line:

b_spec.rb
describe 'B1_describe' do
describe 'B1.1_describe' do
xit 'B1.1.1 test example' do
expect(1).to eq 1
end
it 'B1.1.2 test example' do
Process.kill("INT", Process.pid)
end
end
end

The non-main thread was stuck at the time due to the following sleep in the a_spec.rb:6 line:

a_spec.rb
describe 'A_describe' do
it 'A1 test example' do
expect(1).to eq 1

Thread.new do
sleep 10
end
end
end

Use the above approach to discover the root issue of hanging tests in your test suite. See the usual suspects.

tip

You can use the timeout program to send the TERM signal if the CI provider does not kill the frozen process.

timeout --signal=TERM 600 bundle exec rake "knapsack_pro:queue:rspec[--format d]"

Notice that 600 is in seconds. It sends the signal after 10 minutes of running the command. You may want to adjust that number to ensure the TERM signal is sent after the process is stuck and not before.

For example, you could use 600 seconds (10 minutes) if you know that your tests take no more than 7 minutes to run.

tip

Check your CI provider documentation to configure a timeout if you worry about wasting resources on the hanging CI node. For example, GitHub Actions uses:

You might still need to use the timeout program to terminate tests and capture the backtrace before the CI kills the node completely. Ensure the duration configured for the timeout program is lower than the one configured for the CI provider.

Knapsack Pro does not work on a forked repository​

caution

Make sure your Knapsack Pro API token is set up as a secret in your CI (not hardcoded in the repository) to avoid leaking it.

Since the token won't be available on forked CI builds, you can use a script to run:

  • Knapsack Pro in Fallback Mode(static split by file names) on forked builds
  • Knapsack Pro in Normal Mode on internal builds

Create bin/knapsack_pro_tests, make it executable chmod u+x, and use it on CI to run your tests:

#!/bin/bash

if [ "$KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC" = "" ]; then
KNAPSACK_PRO_ENDPOINT=https://api-disabled-for-fork.knapsackpro.com \
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC=disabled-for-fork \
KNAPSACK_PRO_MAX_REQUEST_RETRIES=0 \
KNAPSACK_PRO_CI_NODE_RETRY_COUNT=0 \
bundle exec rake knapsack_pro:queue:rspec
else
bundle exec rake knapsack_pro:queue:rspec
fi

Error commit_hash parameter is required​

Knapsack Pro takes KNAPSACK_PRO_COMMIT_HASH and KNAPSACK_PRO_BRANCH from the CI environment (see supported CIs). If your CI is not supported you may see the following error:

ERROR -- : [knapsack_pro] {"errors"=>[{"commit_hash"=>["parameter is required"]}]}

In such a case, you have two options:

LoadError: cannot load such file -- spec_helper​

If you are using a complex KNAPSACK_PRO_TEST_FILE_PATTERN, Knapsack Pro could have problems finding the directory containing the spec_helper.rb file. Please set KNAPSACK_PRO_TEST_DIR.

CI builds fail with Test::Unit but all tests passed​

Please ensure you are using Test::Unit only and not loading minitest too.

Tests hitting external APIs fail​

Most likely, you have global shared state between your tests.

For example, you may have a hook that deletes Stripe data while a parallel CI node is testing the Stripe integration:

before(:each) do
Stripe::Subscription.all.each { |subscription| subscription.delete }
Stripe::Customer.all.each { |customer| customer.delete }
end

You can fix it by either:

  • Using a gem like VCR to record/replay your HTTP interactions
  • Write your tests so that they can run in parallel (e.g., each test creates/deletes its own Stripe data)

WebMock/VCR​

WebMock or VCR may trigger the following errors when Knapsack Pro interacts with the Knapsack Pro API:

  • "Real HTTP connections are disabled"
  • WebMock::NetConnectNotAllowedError
  • VCR::Errors::UnhandledHTTPRequestError
  • OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=error: tlsv1 alert internal error

Update the knapsack_pro gem to the latest version to fix those issues.

ActiveRecord::SubclassNotFound​

If files are changing during a tests run, you may get the following error:

ActiveRecord::SubclassNotFound:
Invalid single-table inheritance type: AuthenticationProviders::AnAuthenticationProvider is not a subclass of AuthenticationProvider

Please make sure classes are loaded once in the test environment:

# environments/test.rb

config.eager_load = true

Adding parallel CI nodes/jobs makes test slower​

You can verify if the tests are getting slower on your Knapsack Pro dashboard:

  • Execution times of your CI builds are increasing: Recorded CI builds > Show (build) > Test Files > Total execution time
  • Individual test stats are trending up: Statistics of test files history > Stats (test file) > History of the test file (chart)

Here are the most common reasons:

  • CI nodes share resources (CPU/RAM/IO)
  • Tests are accessing the same resource (e.g., Stripe sandbox, database)
  • Your CI gives you a limited pool of parallel CI nodes (and runs the rest serially)

FactoryBot/FactoryGirl raises​

  • Use the knapsack_pro binary:

    bundle exec knapsack_pro queue:rspec
  • Avoid implicit associations:

    # ⛔️ Bad
    FactoryBot.define do
    factory :assignment do
    task
    end
    end

    # ✅ Good
    FactoryBot.define do
    factory :assignment do
    association :task
    end
    end

Rake tasks under tests are run more than once​

Update the knapsack_pro gem to the latest version to fix this issue.

Execution times are all 0 seconds in Minitest​

When using minitest-spec-rails, avoid the unsupported outer describe. Otherwise, Knapsack Pro won't be able to track the execution time of each test.

# ⛔️ Bad
require 'test_helper'
describe User do
end

# ✅ Good
require 'test_helper'
class UserTest < ActiveSupport::TestCase
let(:user_ken) { User.create!(email: 'ken@example.com') }

it 'works' do
expect(user_ken).must_be_instance_of User
end
end