risingwavelabs/risingwave · error

the target_dir must not include source_dir

Error message

the target_dir must not include source_dir

What it means

After trimming trailing slashes, the migration command checks that target_dir is not nested inside source_dir. If it were, moving objects would overwrite or corrupt the source being iterated, so it refuses to run.

Source

Thrown at src/ctl/src/cmd_impl/hummock/migrate_legacy_object.rs:44

use risingwave_object_store::object::{
    ObjectStoreImpl, OpendalObjectStore, build_remote_object_store,
};

pub async fn migrate_legacy_object(
    url: String,
    source_dir: String,
    target_dir: String,
    concurrency: u32,
) -> anyhow::Result<()> {
    let source_dir = source_dir.trim_end_matches('/');
    let target_dir = target_dir.trim_end_matches('/');
    println!("Normalized source_dir: {source_dir}.");
    println!("Normalized target_dir: {target_dir}.");
    if source_dir.is_empty() || target_dir.is_empty() {
        return Err(anyhow!("the source_dir and target_dir must not be empty"));
    }
    if target_dir.starts_with(source_dir) {
        return Err(anyhow!("the target_dir must not include source_dir"));
    }
    let mut config = ObjectStoreConfig::default();
    config.s3.developer.use_opendal = true;
    let store = build_remote_object_store(
        &url,
        ObjectStoreMetrics::unused().into(),
        "migrate_legacy_object",
        config.into(),
    )
    .await;
    let ObjectStoreImpl::Opendal(opendal) = store else {
        return Err(anyhow!("OpenDAL is required"));
    };
    let mut iter = opendal.list(source_dir, None, None).await?;
    let mut count = 0;
    println!("Migration is started: from {source_dir} to {target_dir}.");
    let mut from_to = Vec::with_capacity(concurrency as usize);
    let timer = Instant::now();

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Choose a target_dir that does not begin with source_dir (e.g. source 'legacy', target 'objects')
  2. Swap direction if you intended the reverse nesting — that is also rejected the same way
  3. Copy to a separate bucket/prefix if a nested layout is genuinely required

Example fix

// before
--source-dir 'hummock' --target-dir 'hummock/v2'
// after
--source-dir 'hummock' --target-dir 'hummock-v2'
Defensive patterns

Strategy: validation

Validate before calling

if target_dir.trim_end_matches('/').starts_with(source_dir.trim_end_matches('/')) {
    panic!("target_dir must not be nested within source_dir");
}

Prevention

When it happens

Trigger: Running migrate-legacy-object with target_dir starting with source_dir, e.g. source 'data' and target 'data/new', or source 'a' and target 'a/b'.

Common situations: Migrating to a subdirectory of the legacy layout; typos where target accidentally shares the source prefix; planning an in-place layout change.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/de2da9746359ed32. Report an issue: GitHub.