{"record":{"id":"6510aef825152816","repo":"influxdata/influxdb","slug":"plugin-code-contains-null-bytes","errorCode":null,"errorMessage":"plugin code contains null bytes","messagePattern":"plugin code contains null bytes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"influxdb3_py_api/src/system_py.rs","lineNumber":296,"sourceCode":"/// For multi-file plugins (when `plugin_root` is `Some`), imports the module and retrieves\n/// the function. For single-file plugins, executes the code in an isolated namespace and\n/// retrieves the function.\nfn load_plugin_function<'py>(\n    py: Python<'py>,\n    code: &str,\n    plugin_root: Option<&Path>,\n    call_site: &str,\n    missing_fn_error: ExecutePluginError,\n) -> Result<Bound<'py, PyAny>, ExecutePluginError> {\n    if let Some(root_path) = plugin_root {\n        load_function_from_module(py, root_path, call_site, missing_fn_error)\n    } else {\n        // Create isolated globals for this plugin execution. Without this, single-file\n        // plugins would share __main__'s namespace and could overwrite each other's\n        // function definitions during concurrent execution.\n        let globals = PyDict::new(py);\n        let code = CString::new(code)\n            .map_err(|e| anyhow::Error::new(e).context(\"plugin code contains null bytes\"))?;\n        let call_site = CString::new(call_site)\n            .map_err(|e| anyhow::Error::new(e).context(\"call site contains null bytes\"))?;\n        py.run(&code, Some(&globals), None)\n            .map_err(anyhow::Error::from)?;\n        py.eval(&call_site, Some(&globals), None)\n            .map_err(|_| missing_fn_error)\n    }\n}\n\n/// Serializes temporary `sys.path` mutation during multi-file plugin imports.\nstatic SYS_PATH_LOCK: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));\n\n/// True when `err` is a `ModuleNotFoundError` for `module_name` itself, so no\n/// plugin code has run and re-importing is side-effect free.\nfn is_module_not_found(py: Python<'_>, err: &PyErr, module_name: &str) -> bool {\n    err.is_instance_of::<PyModuleNotFoundError>(py)\n        && err\n            .value(py)","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/influxdata/influxdb/blob/d28e26e048401c53cbb98cf2d6ab0cf1e98048ca/influxdb3_py_api/src/system_py.rs#L278-L314","documentation":"Single-file plugins (no plugin_root) are executed by passing their source to py.run after converting it with CString::new, and C strings cannot contain NUL bytes. If the stored plugin code contains a single '\\0', CString::new fails and the error is wrapped with context 'plugin code contains null bytes'. In practice this means the uploaded 'source' is not text at all — it is binary, corrupt, or wrongly encoded.","triggerScenarios":"Uploading a compiled/binary artifact (e.g. a .pyc) or a file saved as UTF-16 (whose byte stream contains NULs) as single-file plugin code; a truncated or binary-corrupted transfer leaving stray NUL bytes in the .py file.","commonSituations":"Editor or pipeline re-saving the plugin with UTF-16 encoding; build steps that publish compiled artifacts; copy-paste through tools that mangle encoding; accidental upload of a zip/gz file.","solutions":["Re-save the plugin file as UTF-8 (no BOM) and re-upload","Verify before upload: the file contains no NUL bytes (check b'\\x00' in data)","If you meant to ship compiled code, don't — the engine executes plain .py source; multi-file plugin directories are the packaging mechanism"],"exampleFix":"# before: plugin.py saved as UTF-16 / binary -> 'plugin code contains null bytes'\n\n# after: verify plain UTF-8 text before upload\nsrc = open(\"plugin.py\", \"rb\").read()\nassert b\"\\x00\" not in src, \"not valid UTF-8 text\"\nupload(plugin=\"plugin.py\")","handlingStrategy":"validation","validationCode":"src = open(\"plugin.py\", \"rb\").read()\nassert b\"\\x00\" not in src, \"plugin file contains NUL bytes — re-save as UTF-8 text\"\nsrc.decode(\"utf-8\")  # must succeed\n# only then upload","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Save plugin files as UTF-8 without BOM; avoid UTF-16 encodings from editors on Windows","Upload plain .py source, never .pyc/zip/binary artifacts","Add an encoding check to your plugin upload pipeline"],"tags":["influxdb","plugins","python","null-bytes","cstring","encoding"],"backgroundTag":"null-byte-in-source","analyzedSha":"d28e26e048401c53cbb98cf2d6ab0cf1e98048ca","analyzedAt":"2026-08-16T19:53:34.623Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}