windmill-labs/windmill · error

Couldn't write text file at {}: {}

Error message

Couldn't write text file at {}: {}

What it means

Windmill's Ansible worker creates file resources inside the job directory before running the playbook. When write_file_at_user_defined_location cannot write the text content at the user-specified target path, the worker wraps the underlying IO error in this message. It aborts the job's file-resource setup so the playbook never runs with missing files.

Source

Thrown at backend/windmill-worker/src/ansible_executor.rs:2295

        )
        .map_err(|e| anyhow!("Couldn't write inventory: {}", e))?;

        nsjail_mounts.push(
            define_nsjail_mount(job_dir, &validated_path)
                .map_err(|e| anyhow!("Inventory path (a.k.a. `name`) is invalid: {}", e))?,
        );

        logs.push_str(&format!("\nCreated inventory `{}`", inventory.name));
    }

    for file_res in &r.file_resources {
        let r =
            get_resource_or_variable_content(client, &file_res.resource_path, job_id.to_string())
                .await?;
        let path = file_res.target_path.clone();
        let validated_path =
            write_file_at_user_defined_location(job_dir, path.as_str(), &r, file_res.mode)
                .map_err(|e| anyhow!("Couldn't write text file at {}: {}", path, e))?;

        nsjail_mounts.push(
            define_nsjail_mount(job_dir, &validated_path)
                .map_err(|e| anyhow!("File resource path is invalid: {}", e))?,
        );

        logs.push_str(&format!(
            "\nCreated {} from {:?}",
            file_res.target_path, file_res.resource_path
        ));
    }
    append_logs(job_id, w_id, logs, conn).await;

    Ok(nsjail_mounts)
}

async fn get_resource_or_variable_content(
    client: &AuthedClient,

View on GitHub (pinned to e474e8803c)

Solutions

  1. Fix the file resource's target_path to be a relative path inside the job directory
  2. Check the underlying OS error appended to the message (permissions, ENOSPC, EISDIR) and correct the target
  3. Verify the resource content resolves (the preceding get_resource_or_variable_content succeeded) and that the file resource 'mode' is a valid octal mode like '644'
  4. If the worker filesystem is full, free space or move the worker's job dirs to larger storage

Example fix

// before
{"target_path": "../../../etc/evil.txt", "resource_path": "u/admin/notes"}
// after
{"target_path": "files/notes.txt", "resource_path": "u/admin/notes"}
Defensive patterns

Strategy: validation

Validate before calling

const p = fileRes.target_path;
if (!p || p.includes('..') || path.isAbsolute(p)) {
  throw new Error(`target_path must be a relative path inside the job dir: ${p}`);
}

Type guard

function isSafeRelativePath(p: string): boolean {
  return p.length > 0 && !path.isAbsolute(p) && !p.split('/').includes('..');
}

Prevention

When it happens

Trigger: A script/flow job with file resources whose target_path points outside the allowed job directory, is a directory rather than a file, contains invalid characters, or whose mode string is malformed; also when the job_dir filesystem is full or read-only.

Common situations: Users set a target_path with '..' segments or an absolute path escaping the job dir; the target path collides with an existing directory; disk quota exceeded on the worker node.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/91357972b7c43654. Report an issue: GitHub.