{"record":{"id":"0fcd7d13ea301644","repo":"windmill-labs/windmill","slug":"preprocessor-function-is-missing-0fcd7d","errorCode":null,"errorMessage":"preprocessor function is missing","messagePattern":"preprocessor function is missing","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/windmill-worker/src/python_executor.rs","lineNumber":812,"sourceCode":"        job.preprocessed,\n        job.script_entrypoint_override.as_deref(),\n        inner_content,\n        &script_path,\n        &temp_script_refs,\n    )\n    .await?;\n\n    tracing::debug!(\"Finished preparing wrapper\");\n\n    let apply_preprocessor = pre_spread.is_some();\n\n    create_args_and_out_file(&client, job, job_dir, conn).await?;\n    tracing::debug!(\"Finished preparing wrapper\");\n\n    let preprocessor = if let Some(pre_spread) = pre_spread {\n        format!(\n            r#\"if inner_script.preprocessor is None or not callable(inner_script.preprocessor):\n        raise ValueError(\"preprocessor function is missing\")\n    else:\n        pre_args = {{}}\n        {pre_spread}\n        for k, v in list(pre_args.items()):\n            if v == '<function call>':\n                del pre_args[k]\n        kwargs = inner_script.preprocessor(**pre_args)\n        kwrags_json = res_to_json(kwargs, type(kwargs))\n        with open(\"args.json\", 'w') as f:\n            f.write(kwrags_json)\"#\n        )\n    } else {\n        \"\".to_string()\n    };\n\n    let postprocessor = get_result_postprocessor(annotations.skip_result_postprocessing);\n\n    let os_main_override = if let Some(main_override) = main_name.as_ref() {","sourceCodeStart":794,"sourceCodeEnd":830,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-worker/src/python_executor.rs#L794-L830","documentation":"backend/windmill-worker/src/python_executor.rs builds a Python wrapper for the job. When the job has a preprocessor expansion (pre_spread), the generated wrapper checks inner_script.preprocessor; if it is None or not callable it raises ValueError('preprocessor function is missing'). This happens when preprocessor args are supplied but the script itself defines no usable preprocessor function.","triggerScenarios":"A job runs with preprocessor-related arguments (pre_spread present) but the deployed script's preprocessor field is None or not callable — e.g. the script was saved without a preprocessor, or its preprocessor was set to a non-function value.","commonSituations":"Editing a script and dropping the preprocessor definition while keeping preprocessor inputs; a preprocessor step removed/replaced upstream but the job payload still requests preprocessing; desync between script version and the flow step that passes preprocessor args.","solutions":["Open the script and define a callable preprocessor function (the wrapper requires inner_script.preprocessor to be callable)","Remove the preprocessor inputs/args from the job or step if preprocessing is no longer needed","Re-save/redeploy the script so the deployed version matches the flow step's expectations","Inspect '<function call>' placeholder handling: args whose value equals that marker are deleted, ensure you are not passing raw placeholders expecting a preprocessor to consume them"],"exampleFix":"# before: script has no preprocessor but step passes preprocessor args\n# after: add to the script\ndef preprocessor(args):\n    ...  # transform args\n    return args","handlingStrategy":"validation","validationCode":"# before deploying/running a job that uses preprocessor args\nassert callable(getattr(script, 'preprocessor', None)), \"script must define a callable preprocessor\"","typeGuard":"def has_callable_preprocessor(script) -> bool:\n    pre = getattr(script, 'preprocessor', None)\n    return callable(pre)","tryCatchPattern":"try {\n    runJob(payload)\n} catch (e) {\n    if (String(e).includes('preprocessor function is missing')) {\n        // redeploy the script with a preprocessor or drop the preprocessor args\n    } else { throw e }\n}","preventionTips":["Never remove a preprocessor definition while the step still passes preprocessor args","Validate the deployed script's preprocessor field when saving","Keep flow steps and script versions in sync after refactors","Add a lint/CI check that scripts consuming preprocessor args define a callable preprocessor"],"tags":["python","windmill-worker","job-execution","preprocessor","script-config"],"backgroundTag":"missing-preprocessor-function","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"}