{"record":{"id":"f2fa80b9faae588a","repo":"influxdata/influxdb","slug":"expected-a-dictionary-in-header-item","errorCode":null,"errorMessage":"expected a dictionary in header item","messagePattern":"expected a dictionary in header item","errorType":"error_code","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"influxdb3_py_api/src/system_py.rs","lineNumber":666,"sourceCode":"            // Handle status and/or headers if provided\n            if tuple_len >= 2 {\n                let second = tuple.get_item(1)?;\n\n                // Check if the second item is a status code or headers\n                if let Ok(status_code) = second.extract::<i64>() {\n                    status = status_code.try_into().unwrap_or(500);\n                } else if let Ok(header_dict) = second.cast::<PyDict>() {\n                    // It's headers\n                    let additional_headers: std::collections::HashMap<String, String> =\n                        header_dict.extract()?;\n                    headers.extend(additional_headers);\n                }\n\n                // If there's a third item, it must be headers\n                if tuple_len == 3 {\n                    let header_item = tuple.get_item(2)?;\n                    let Ok(header_dict) = header_item.cast::<PyDict>() else {\n                        bail!(\"expected a dictionary in header item\");\n                    };\n                    let additional_headers: std::collections::HashMap<String, String> =\n                        header_dict.extract()?;\n                    headers.extend(additional_headers);\n                }\n            }\n\n            return Ok((status, headers, response_body));\n        }\n    }\n\n    // Check if it's a string\n    if let Ok(string_val) = result.extract::<String>() {\n        let mut headers = default_headers.clone();\n        headers.insert(\"Content-Type\".to_string(), default_mimetype.to_string());\n        return Ok((default_status, headers, string_val));\n    }\n","sourceCodeStart":648,"sourceCodeEnd":684,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/influxdb3_py_api/src/system_py.rs#L648-L684","documentation":"When processing a plugin return value under Flask conventions, a 3-element tuple is interpreted as (body, status_or_headers, headers). If the third element is not a Python dict, process_flask_response bails with 'expected a dictionary in header item'. The library requires the third tuple slot to be a dict of header name/value string pairs.","triggerScenarios":"A request-trigger plugin returns a 3-tuple whose third element is not a dict: e.g. return 'body', 200, [('Content-Type','text/html')] (Flask-style list of tuples), a headers object, None, or a string.","commonSituations":"Developers copy Flask patterns where headers may be a list of (key, value) tuples; they pass a Flask Headers object or a list instead of a plain dict; they accidentally return a 3-tuple where the third element was meant to be something else.","solutions":["Change the third tuple element to a plain dict of string->string headers, e.g. ('body', 200, {'Content-Type': 'text/html'})","Return only 2 elements (body, status) if you have no extra headers","If headers come from Flask as a list of pairs, convert with dict(headers) before returning"],"exampleFix":"// before (plugin)\nreturn 'ok', 200, [('Content-Type', 'text/html')]\n// after (plugin)\nreturn 'ok', 200, {'Content-Type': 'text/html'}","handlingStrategy":"type-guard","validationCode":"def _ok_headers(h):\n    return isinstance(h, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in h.items())","typeGuard":"def is_header_dict(h):\n    return isinstance(h, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in h.items())","tryCatchPattern":null,"preventionTips":["Always return headers as a plain dict of str->str, never a list of tuples or Headers object","Return at most a 3-element tuple: (body, [status|headers], [headers])","Add a unit test asserting the exact return shape of your plugin"],"tags":["python","plugin-execution","response-format","flask"],"backgroundTag":"type-mismatch","analyzedSha":"06200ef96ba82c5f6727e5038a83af8e722c6875","analyzedAt":"2026-09-19T12:55:30.003Z","contentChangedAt":"2026-09-19T12:55:30.003Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}