{"record":{"id":"91357972b7c43654","repo":"windmill-labs/windmill","slug":"couldn-t-write-text-file-at","errorCode":null,"errorMessage":"Couldn't write text file at {}: {}","messagePattern":"Couldn't write text file at (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-worker/src/ansible_executor.rs","lineNumber":2295,"sourceCode":"        )\n        .map_err(|e| anyhow!(\"Couldn't write inventory: {}\", e))?;\n\n        nsjail_mounts.push(\n            define_nsjail_mount(job_dir, &validated_path)\n                .map_err(|e| anyhow!(\"Inventory path (a.k.a. `name`) is invalid: {}\", e))?,\n        );\n\n        logs.push_str(&format!(\"\\nCreated inventory `{}`\", inventory.name));\n    }\n\n    for file_res in &r.file_resources {\n        let r =\n            get_resource_or_variable_content(client, &file_res.resource_path, job_id.to_string())\n                .await?;\n        let path = file_res.target_path.clone();\n        let validated_path =\n            write_file_at_user_defined_location(job_dir, path.as_str(), &r, file_res.mode)\n                .map_err(|e| anyhow!(\"Couldn't write text file at {}: {}\", path, e))?;\n\n        nsjail_mounts.push(\n            define_nsjail_mount(job_dir, &validated_path)\n                .map_err(|e| anyhow!(\"File resource path is invalid: {}\", e))?,\n        );\n\n        logs.push_str(&format!(\n            \"\\nCreated {} from {:?}\",\n            file_res.target_path, file_res.resource_path\n        ));\n    }\n    append_logs(job_id, w_id, logs, conn).await;\n\n    Ok(nsjail_mounts)\n}\n\nasync fn get_resource_or_variable_content(\n    client: &AuthedClient,","sourceCodeStart":2277,"sourceCodeEnd":2313,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-worker/src/ansible_executor.rs#L2277-L2313","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the file resource's target_path to be a relative path inside the job directory","Check the underlying OS error appended to the message (permissions, ENOSPC, EISDIR) and correct the target","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'","If the worker filesystem is full, free space or move the worker's job dirs to larger storage"],"exampleFix":"// before\n{\"target_path\": \"../../../etc/evil.txt\", \"resource_path\": \"u/admin/notes\"}\n// after\n{\"target_path\": \"files/notes.txt\", \"resource_path\": \"u/admin/notes\"}","handlingStrategy":"validation","validationCode":"const p = fileRes.target_path;\nif (!p || p.includes('..') || path.isAbsolute(p)) {\n  throw new Error(`target_path must be a relative path inside the job dir: ${p}`);\n}","typeGuard":"function isSafeRelativePath(p: string): boolean {\n  return p.length > 0 && !path.isAbsolute(p) && !p.split('/').includes('..');\n}","tryCatchPattern":null,"preventionTips":["Keep target_path relative and free of '..' segments","Validate file resource modes are octal strings","Monitor worker disk usage"],"tags":["filesystem","ansible","path-validation"],"backgroundTag":"invalid-file-path","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}