influxdata/influxdb · info

must have offset

Error message

must have offset

What it means

StringBuffer::pop() removes and returns the last packed string. It first returns None when offsets.len() < 2 (buffer empty), then pops one offset and reads the new tail offset to know where the previous string ends. At that point offsets is guaranteed to contain at least the initial 0 offset, so .expect('must have offset') is a defensive guard that is unreachable in correct code — the len < 2 check already established at least two offsets existed before the pop.

Source

Thrown at core/table_batch/src/builder/column_writer/string.rs:40

            offsets: vec![0, value.len() as u32],
        })
    }

    pub(crate) fn push_str(&mut self, s: &str) {
        self.0.values.push_str(s);
        self.0.offsets.push(self.0.values.len() as u32);
    }

    /// Remove the last wrote value and return it.
    ///
    /// This call does not panic when empty.
    pub(crate) fn pop(&mut self) -> Option<String> {
        if self.0.offsets.len() < 2 {
            return None;
        }

        self.0.offsets.pop();
        let new_len = *self.0.offsets.last().expect("must have offset") as usize;

        let last_val = self.0.values.split_at(new_len).1.to_string();
        self.0.values.truncate(new_len);

        Some(last_val)
    }

    pub(crate) fn len(&self) -> usize {
        self.0.offsets.len() - 1
    }

    pub(crate) fn finish(self) -> Option<PackedStrings> {
        if self.0.offsets.len() < 2 {
            None
        } else {
            Some(self.0)
        }
    }

View on GitHub (pinned to d28e26e048)

Solutions

  1. If hit, grep for direct manipulation of the .0 / PackedStrings fields (values/offsets) outside StringBuffer and route all mutation through push_str/pop.
  2. Treat it as an internal bug in core/table_batch and report with the write/rollback sequence that preceded it.
  3. Keep pop()'s Option-based API: callers should already handle None, so no caller change is needed.
Defensive patterns

Strategy: validation

Validate before calling

// pop() already returns Option on empty — use the Option API instead of assuming:
if let Some(last) = buffer.pop() {
    // handle last
}

Prevention

When it happens

Trigger: Calling pop() on a StringBuffer; the expect itself cannot fire through the public flow because offsets always retains its initial 0 element (Default initializes offsets to vec![0] and every push appends). It could only fire if the pub(crate) PackedStrings field was mutated directly elsewhere leaving offsets empty.

Common situations: Essentially never observed; the reachable sibling failure is calling pop() on an empty buffer, which correctly returns None. If this message appears, look for code reaching into the pub(crate) tuple field (.0) and truncating offsets directly, bypassing push_str/pop.

Related errors


AI-assisted analysis of influxdata/influxdb@d28e26e048 (2026-08-16). Data as JSON: /api/errors/4e1731ad51569816. Report an issue: GitHub.