neondatabase/neon · error

Invalid time for travel_to: '{}'

Error message

Invalid time for travel_to: '{}'

What it means

The time-travel-remote-prefix command requires --travel_to as an RFC 3339 timestamp and parses it with humantime::parse_rfc3339. Anything that is not full RFC 3339, such as date-only strings, slash-separated dates, or epoch numbers, is rejected with this error.

Source

Thrown at pageserver/ctl/src/main.rs:157

        }
        Commands::DrawTimeline {} => {
            draw_timeline_dir::main()?;
        }
        Commands::AnalyzeLayerMap(cmd) => {
            layer_map_analyzer::main(&cmd).await?;
        }
        Commands::PrintLayerFile(cmd) => {
            if let Err(e) = read_pg_control_file(&cmd.path) {
                println!(
                    "Failed to read input file as a pg control one: {e:#}\n\
                    Attempting to read it as layer file"
                );
                print_layerfile(&cmd.path).await?;
            }
        }
        Commands::TimeTravelRemotePrefix(cmd) => {
            let timestamp = humantime::parse_rfc3339(&cmd.travel_to)
                .map_err(|_e| anyhow::anyhow!("Invalid time for travel_to: '{}'", cmd.travel_to))?;

            let done_if_after = if let Some(done_if_after) = &cmd.done_if_after {
                humantime::parse_rfc3339(done_if_after).map_err(|_e| {
                    anyhow::anyhow!("Invalid time for done_if_after: '{}'", done_if_after)
                })?
            } else {
                const SAFETY_MARGIN: Duration = Duration::from_secs(3);
                tokio::time::sleep(SAFETY_MARGIN).await;
                // Convert to string representation and back to get rid of sub-second values
                let done_if_after = SystemTime::now();
                tokio::time::sleep(SAFETY_MARGIN).await;
                done_if_after
            };

            let timestamp = strip_subsecond(timestamp);
            let done_if_after = strip_subsecond(done_if_after);

            let Some(prefix) = validate_prefix(&cmd.prefix) else {

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Use a full RFC 3339 timestamp with offset: 2024-01-01T12:34:56Z or 2024-01-01T12:34:56+00:00.
  2. Generate timestamps in scripts with date -u +%Y-%m-%dT%H:%M:%SZ.

Example fix

# before
pageserver_ctl time-travel-remote-prefix --travel-to "2024/01/01 00:00" ...

# after
pageserver_ctl time-travel-remote-prefix --travel-to "2024-01-01T00:00:00Z" ...
Defensive patterns

Strategy: validation

Validate before calling

// validate before invoking the ctl
anyhow::ensure!(
    humantime::parse_rfc3339(&travel_to).is_ok(),
    "travel_to must be RFC 3339, e.g. 2024-01-01T00:00:00Z"
);

Type guard

fn is_rfc3339(s: &str) -> bool {
    humantime::parse_rfc3339(s).is_ok()
}

Prevention

When it happens

Trigger: Passing --travel-to "2024/01/01", "2024-01-01" without time and timezone, or an epoch integer; only forms like 2024-01-01T00:00:00Z parse.

Common situations: Timestamps generated with date +%F (date only) or locale-specific formats; hand-edited shell history.

Related errors


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/42761044b3e75fcc. Report an issue: GitHub.