Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@ryzhyk
Copy link
Contributor

@ryzhyk ryzhyk commented Jan 25, 2026

A user reported a panic with the following call stack:

thread 'circuit-thread' (17) panicked at /home/ubuntu/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/itertools-0.14.0/src/format.rs:95:21:
Format: was already formatted once
stack backtrace:
   0: std::panicking::begin_panic
   1: <itertools::format::Format<I> as core::fmt::Display>::fmt
   2: core::fmt::write
   3: core::fmt::write
   4: alloc::fmt::format::format_inner
   5: <sentry_tracing::converters::FieldVisitor as tracing_core::field::Visit>::record_debug
   6: tracing_core::field::ValueSet::record
   7: sentry_tracing::converters::extract_event_data
   8: sentry_tracing::converters::extract_event_data_with_context
   9: sentry_tracing::converters::breadcrumb_from_event
  10: <sentry_tracing::layer::SentryLayer<S> as tracing_subscriber::layer::Layer<S>>::on_event
  11: tracing_core::event::Event::dispatch
  12: dbsp_adapters::util::LongOperationWarning::check
  13: dbsp_adapters::controller::CircuitThread::run
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

which points to this line:

            self.checkpoint_delay_warning
                .get_or_insert_with(|| LongOperationWarning::new(Duration::from_secs(1)))
                .check(|elapsed| {
                    info!(
                        "checkpoint delayed {} seconds because of: {}",
                        elapsed.as_secs(),
                        reasons.iter().format(", ")
                    )
                });

The panic can happen if the iterator returned by the format method is evaluated twice. This happens inside the internals of the tracing crate. Not sure why we haven't seen this in the past. The fix just converts the iterator to string, so there is no way it can get evaluated twice.

A user reported a panic with the following call stack:

```
thread 'circuit-thread' (17) panicked at /home/ubuntu/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/itertools-0.14.0/src/format.rs:95:21:
Format: was already formatted once
stack backtrace:
   0: std::panicking::begin_panic
   1: <itertools::format::Format<I> as core::fmt::Display>::fmt
   2: core::fmt::write
   3: core::fmt::write
   4: alloc::fmt::format::format_inner
   5: <sentry_tracing::converters::FieldVisitor as tracing_core::field::Visit>::record_debug
   6: tracing_core::field::ValueSet::record
   7: sentry_tracing::converters::extract_event_data
   8: sentry_tracing::converters::extract_event_data_with_context
   9: sentry_tracing::converters::breadcrumb_from_event
  10: <sentry_tracing::layer::SentryLayer<S> as tracing_subscriber::layer::Layer<S>>::on_event
  11: tracing_core::event::Event::dispatch
  12: dbsp_adapters::util::LongOperationWarning::check
  13: dbsp_adapters::controller::CircuitThread::run
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
```

which points to this line:

```rust
            self.checkpoint_delay_warning
                .get_or_insert_with(|| LongOperationWarning::new(Duration::from_secs(1)))
                .check(|elapsed| {
                    info!(
                        "checkpoint delayed {} seconds because of: {}",
                        elapsed.as_secs(),
                        reasons.iter().format(", ")
                    )
                });

```

The panic can happen if the iterator returned by the `format` method is
evaluated twice. This happens inside the internals of the tracing crate. Not
sure why we haven't seen this in the past. The fix just converts the iterator
to string, so there is no way it can get evaluated twice.

Signed-off-by: Leonid Ryzhyk <[email protected]>
@ryzhyk ryzhyk requested review from blp and Copilot January 25, 2026 23:40
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a panic in the logging path caused by re-evaluation of an itertools::format iterator within the tracing infrastructure. The change eagerly materializes the formatted reasons into a String before passing it to the info! macro, preventing multiple evaluations.

Changes:

  • Adjust LongOperationWarning::check logging to call to_string() on reasons.iter().format(", "), avoiding double-use of the Format iterator.
  • As a result, the logged reasons are now passed as an owned String instead of a lazily formatted iterator.

@ryzhyk ryzhyk enabled auto-merge January 25, 2026 23:40
Copy link
Member

@blp blp left a comment

Choose a reason for hiding this comment

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

Good catch. I'll look out for that footgun now.

@ryzhyk ryzhyk added this pull request to the merge queue Jan 25, 2026
Merged via the queue into main with commit d78eda6 Jan 26, 2026
6 of 7 checks passed
@ryzhyk ryzhyk deleted the info_panic branch January 26, 2026 00:52
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.

3 participants