{"record":{"id":"9b74f47faaeeb73a","repo":"windmill-labs/windmill","slug":"empty-parameter-name-in-path","errorCode":null,"errorMessage":"Empty parameter name in path: {}","messagePattern":"Empty parameter name in path: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-api-openapi/src/lib.rs","lineNumber":204,"sourceCode":"            security_scheme,\n            args_schema,\n        }\n    }\n}\n\nfn from_route_path_to_openapi_path(\n    route_path: &str,\n    kind: &Kind,\n) -> Result<(Vec<String>, Option<Value>)> {\n    let mut openapi_path = String::new();\n    let mut parameters = Vec::new();\n\n    for segment in route_path.split('/') {\n        if segment.starts_with(':') {\n            let param_name = &segment[1..];\n\n            if param_name.is_empty() {\n                return Err(anyhow!(\"Empty parameter name in path: {}\", route_path).into());\n            }\n\n            openapi_path.push_str(&format!(\"/{{{}}}\", param_name));\n            parameters.push(serde_json::json!({\n                \"name\": param_name,\n                \"in\": \"path\",\n                \"required\": true,\n                \"schema\": { \"type\": \"string\" }\n            }));\n        } else if !segment.is_empty() {\n            openapi_path.push('/');\n            openapi_path.push_str(segment);\n        } else {\n            openapi_path.push('/');\n        }\n    }\n\n    let parameters_json = if parameters.is_empty() {","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-api-openapi/src/lib.rs#L186-L222","documentation":"from_route_path_to_openapi_path converts Windmill's route syntax (':param' segments) into OpenAPI '{param}' syntax. When a route path contains a segment starting with ':' but with no name after it (e.g. '/api/foo/:/bar'), the conversion fails with 'Empty parameter name in path: {}'. This is a guard against generating invalid OpenAPI path templates.","triggerScenarios":"A route registered with a path containing a bare ':' segment or a trailing ':' (e.g. 'prefix/:') passed into generate_paths -> from_route_path_to_openapi_path.","commonSituations":"Programmatic path construction bugs where a variable holding the param name is empty; typos in route definitions; concatenating path fragments that leave a dangling colon.","solutions":["Fix the route definition to give the parameter a name (e.g. /:workspace_id instead of /:)","Check code that builds route paths dynamically for empty string variables","Add a registration-time assertion rejecting routes with empty parameter names"],"exampleFix":"// before\nlet route = format!(\"/api/w/{}/run/:\", workspace); // dangling colon\n// after\nlet route = format!(\"/api/w/{}/run/:job_id\", workspace);","handlingStrategy":"validation","validationCode":"fn validate_route_path(p: &str) -> Result<(), String> {\n    for seg in p.split('/') {\n        if seg.starts_with(':') && seg.len() <= 1 {\n            return Err(format!(\"empty parameter name in path {p}\"));\n        }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"match from_route_path_to_openapi_path(path) {\n    Ok(openapi_path) => openapi_path,\n    Err(e) => {\n        tracing::error!(\"route path invalid: {e:#}\");\n        skip_path_and_continue(path)\n    }\n}","preventionTips":["Never build route strings by raw concatenation; use typed route builders","Add unit tests covering every registered route's path format","Assert non-empty param names at route registration time"],"tags":["openapi","routing","rust"],"backgroundTag":"invalid-route-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"}