influxdata/influxdb · error · CodecError

failed to obtain writer handle clone

Error message

failed to obtain writer handle clone: {0}

What it means

CodecError::CloneSink, raised when cloning a handle to the provided write sink (a std::io::Error occurs while try_clone-ing the writer). The library needs a second handle to the sink (e.g. for buffered writing) and the sink type does not support cloning.

Solutions

  1. Use a sink type that supports try_clone, such as std::fs::File or std::io::Cursor
  2. Write to an in-memory buffer (Vec<u8>/Cursor) and upload the bytes afterwards instead of a non-cloneable stream
  3. Fix the custom writer wrapper to implement try_clone correctly
  4. Check the inner std::io::Error for the OS-level reason (e.g. EMFILE, bad fd)

Example fix

// before: non-cloneable sink
let sink = MyStreamWrapper::new(sock);
serialize_to_parquet(sink)?; // CloneSink error
// after
let buf = std::io::Cursor::new(Vec::new());
serialize_to_parquet(buf)?;
upload(buf.into_inner());
Defensive patterns

Strategy: type-guard

Validate before calling

// only pass sinks that can be cloned
fn assert_cloneable<S: Write + Seek + TryClone>(_: &S) {}
// or pre-check: use File/Cursor rather than custom wrappers

Type guard

fn is_cloneable_sink<S: Write + Seek>(s: &S) -> bool {
    // File and Cursor support try_clone; custom wrappers may not
    s.is_file() || std::mem::size_of::<*const S>() == std::mem::size_of::<*const std::fs::File>() && s.is_file()
}

Try / catch

match res {
    Err(CodecError::CloneSink(e)) => {
        error!(%e, "sink not cloneable; use File/Cursor or an in-memory buffer")
    }
    other => other?,
}

Prevention

When it happens

Trigger: Passing a Write+Seek sink whose try_clone() fails (e.g. certain non-cloneable file/socket wrappers) into the Parquet serialisation entry point that clones the handle.

Common situations: Wrapping a writer in a custom type without try_clone support, using a pipe or socket that cannot duplicate its descriptor, or the underlying fd being closed/unavailable.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19). Data as JSON: /api/errors/682d57381239ec68. Report an issue: GitHub.

Appendix: source

Thrown at core/parquet_file/src/serialize.rs:84

    NoRows,

    /// A DataFusion error during the plan execution.
    ///
    /// Of note: a ResourcesExhaused error likely means the buffer
    /// used for parquet data became too large.
    #[error(transparent)]
    DataFusion(Box<DataFusionError>),

    /// Serialising the [`IoxMetadata`] to protobuf-encoded bytes failed.
    #[error("failed to serialize iox metadata: {0}")]
    MetadataSerialisation(#[from] prost::EncodeError),

    /// Writing the parquet file failed with the specified error.
    #[error("failed to build parquet file: {0}")]
    Writer(#[from] ParquetError),

    /// Attempting to clone a handle to the provided write sink failed.
    #[error("failed to obtain writer handle clone: {0}")]
    CloneSink(std::io::Error),
}

impl From<CodecError> for DataFusionError {
    fn from(value: CodecError) -> Self {
        match value {
            e @ (CodecError::NoRecordBatches
            | CodecError::NoRows
            | CodecError::MetadataSerialisation(_)
            | CodecError::CloneSink(_)) => Self::External(Box::new(e)),
            CodecError::Writer(e) => Self::ParquetError(Box::new(e)),
            CodecError::DataFusion(e) => *e,
        }
    }
}

impl From<crate::writer::Error> for CodecError {
    fn from(value: crate::writer::Error) -> Self {

View on GitHub (pinned to 06200ef96b)