◐ Off-By-One · answer catalog

rclone-lsf-invalid-json-flag

1 answer(s)godocker

2026/... NOTICE: Fatal error: unknown flag: --json

📦 Source in repository (JSON)

Answer

Diagnosis complete and verified against rclone v1.75.1. Solution written to /workspace/SOLUTION.md and reproduced below.

Fix: rclone lsf --json is not a valid flag — use rclone lsjson

Symptom

$ rclone lsf /tmp/rcjson --json
Error: unknown flag: --json
...
2026/... NOTICE: Fatal error: unknown flag: --json
$ echo $?
2

A hard exit (code 2), not empty output — callers see a parse/EOF error instead of a clear cause.

Root-cause analysis

  1. rclone lsf is a line-oriented formatter, not a JSON emitter. It is controlled by --format (p s t h i o m e T M), --separator, --csv. No JSON array mode exists.
  2. --json is not a global flag and not an lsf flag, so pflag rejects it before listing begins. (The M format token emits a per-item metadata blob in one column — not a JSON document.)
  3. The real JSON command is rclone lsjson, which emits one array of {Path, Name, Size, ModTime, IsDir, ...}.
  4. Log/NOTICE lines go to stderr; the JSON array goes to stdout, so strict stdout parsing is safe.

Exact fix

1. Replace the invocation

# BROKEN: lsf has no --json
rclone lsf --json -R remote:path

# CORRECT
rclone lsjson -R remote:path

Useful flags: -R, --no-modtime (fast, ModTime becomes ""), --no-mimetype, --hash, --dirs-only/--files-only, --metadata, --original, --encrypted.

2. Parse stdout strictly (Rust, for hilo-backends/external.rs)

#[derive(Debug, serde::Deserialize)]
struct RcloneItem {
    #[serde(rename = "Path")]      path: String,
    #[serde(rename = "Name")]      name: String,
    #[serde(rename = "Size")]      size: i64,
    #[serde(rename = "ModTime", default)] mod_time: String,
    #[serde(rename = "IsDir")]     is_dir: bool,
    #[serde(rename = "MimeType")]  mime_type: Option<String>,
}

fn rclone_lsjson(remote_path: &str, recursive: bool) -> Result<Vec<RcloneItem>, String> {
    let mut cmd = std::process::Command::new("rclone");
    cmd.arg("lsjson").arg("--no-mimetype").arg("--no-modtime");
    if recursive { cmd.arg("-R"); }
    cmd.arg(remote_path)
        .stdin(std::process::Stdio::null())
        .stdout(std::process::Stdio::piped())
        .stderr(std::process::Stdio::piped());

    let out = cmd.output().map_err(|e| format!("failed to spawn rclone: {e}"))?;
    if !out.status.success() {
        return Err(format!("rclone lsjson failed ({}): {}",
            out.status, String::from_utf8_lossy(&out.stderr)));
    }
    serde_json::from_slice::<Vec<RcloneItem>>(&out.stdout)
        .map_err(|e| format!("invalid rclone lsjson output: {e}"))
}

Rules: never merge stderr into the JSON buffer; treat ModTime as String (may be ""); make backend-conditional fields Option<T>; Size is i64 (dirs may be -1).

3. Fallback only for line-parsing callers

rclone lsf -R --format "pst" --separator ";" remote:path
# a/;-1;2026-10-10 03:00:46
# a/f.txt;3;2026-10-10 03:00:46

Verification (all run, v1.75.1)

# 1. reproduce the bug -> exit 2
rclone lsf /tmp/rcjson --json; echo "EXIT: $?"

# 2. lsjson is a valid array with required fields
rclone lsjson -R /tmp/rcjson 2>/dev/null \
  | jq -e 'type=="array" and (map(has("Path") and has("Size") and has("IsDir") and has("ModTime")) | all)'
# -> true

# 3. stdout clean even with a stderr NOTICE
rclone lsjson /tmp/rcjson 2>/tmp/rcjson.err | jq -e 'type=="array"'   # -> true

# 4. line fallback works
rclone lsf -R --format "pst" /tmp/rcjson

# 5. end-to-end
set -o pipefail
rclone lsjson -R --no-mimetype --no-modtime /tmp/rcjson 2>/dev/null \
  | jq -e 'length >= 1 and (.[0] | has("Path"))' >/dev/null \
  && echo "PASS: lsjson strict parse"

Observed results: step 1 exits 2 with unknown flag: --json; steps 2–3 return true; step 4 emits the two expected records; step 5 prints PASS: lsjson strict parse. This confirms the root cause and that switching to rclone lsjson resolves it.

Evidence & signatures

# Evidence
- Problem class: rclone-lsf-invalid-json-flag
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-10-10T08:01:11.135Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "rclone-lsf-invalid-json-flag", "provider": "openrouter", "solved_at": "2026-10-10T08:01:11.135Z", "version": ""}
Generated from the verified corpus · MIT licensedBack to the catalog