{"record":{"id":"52f10564b154318a","repo":"projectdiscovery/nuclei","slug":"failed-to-transform-input-for-protocol-s","errorCode":null,"errorMessage":"failed to transform input for protocol %s","messagePattern":"failed to transform input for protocol (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/tmplexec/flow/flow_internal.go","lineNumber":40,"sourceCode":"\t\tevaluation := f.options.Variables.EvaluateScope(scope)\n\t\tf.options.GetTemplateCtx(f.ctx.Input.MetaInput).Merge(evaluation.Values) // merge all variables into template context\n\t\tf.options.GetTemplateCtx(f.ctx.Input.MetaInput).MergeTemplateVariables(evaluation.TemplateValues)\n\n\t\t// to avoid polling update template variables everytime we execute a protocol\n\t\tm := f.options.GetTemplateCtx(f.ctx.Input.MetaInput).GetAll()\n\t\t_ = runtime.Set(\"template\", m)\n\t}()\n\tmatcherStatus := &atomic.Bool{} // due to interactsh matcher polling logic this needs to be atomic bool\n\t// if no id is passed execute all requests in sequence\n\tif len(opts.reqIDS) == 0 {\n\t\t// execution logic for http()/dns() etc\n\t\tfor index := range f.allProtocols[opts.protoName] {\n\t\t\treq := f.allProtocols[opts.protoName][index]\n\t\t\t// transform input if required\n\t\t\tinputItem := f.ctx.Input.Clone()\n\t\t\tif f.options.InputHelper != nil && f.ctx.Input.MetaInput.Input != \"\" {\n\t\t\t\tif inputItem.MetaInput.Input = f.options.InputHelper.Transform(inputItem.MetaInput.Input, req.Type()); inputItem.MetaInput.Input == \"\" {\n\t\t\t\t\tf.ctx.LogError(fmt.Errorf(\"failed to transform input for protocol %s\", req.Type()))\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t\terr := req.ExecuteWithResults(inputItem, output.InternalEvent(f.options.GetTemplateCtx(f.ctx.Input.MetaInput).GetAll()), output.InternalEvent{}, f.protocolResultCallback(req, matcherStatus, opts))\n\t\t\tif err != nil {\n\t\t\t\t// save all errors in a map with id as key\n\t\t\t\t// its less likely that there will be race condition but just in case\n\t\t\t\tid := req.GetID()\n\t\t\t\tif id == \"\" {\n\t\t\t\t\tid, _ = reqMap.GetKeyWithValue(req)\n\t\t\t\t}\n\t\t\t\terr = f.allErrs.Set(opts.protoName+\":\"+id, err)\n\t\t\t\tif err != nil {\n\t\t\t\t\tf.ctx.LogError(fmt.Errorf(\"failed to store flow runtime errors got %v\", err))\n\t\t\t\t}\n\t\t\t\treturn matcherStatus.Load()\n\t\t\t}\n\t\t}","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/projectdiscovery/nuclei/blob/265b3a3dec374741614e342f813c10f8b38d2bb7/pkg/tmplexec/flow/flow_internal.go#L22-L58","documentation":"In flow_internal.go, when a flow executes a protocol request for a target and an InputHelper is configured, the target input is passed through InputHelper.Transform to shape it for that protocol. If Transform returns an empty string the flow logs 'failed to transform input for protocol %s' and aborts the whole flow (returns false). Transform (pkg/input/transform.go) returns empty for: websocket when the input lacks a ws:// or wss:// prefix, and file/offline-http when the input is not a usable path (e.g. it looks like host:port or a URL).","triggerScenarios":"A flow template that includes a websocket request run against a bare target like `target.com` or `https://target.com` — typeWebsocket requires the ws/wss scheme and returns \"\". Likewise a flow containing file or offline-http requests run against URL/host inputs: typeFilepath rejects inputs with a port and non-existent paths. DNS/WHOIS/HTTP/host:port protocols effectively never return empty.","commonSituations":"Feeding `-l urls.txt` (https:// entries) or `-u host` to a flow template that mixes an HTTP section with a websocket section; running passive/offline-http flow templates against live-scan target lists; expecting nuclei to auto-synthesize ws:// from a bare host the way it does for http.","solutions":["Provide scheme-correct inputs: `ws://target.com` or `wss://target.com` for websocket flow templates","Split the template so each protocol gets inputs it can transform (separate websocket template from http template)","If the input must stay a bare host, remove the websocket/file section from the flow or preprocess inputs to add the scheme","Verify with one host first: `nuclei -u ws://target.com -t flow-template.yaml`"],"exampleFix":"# before\nnuclei -u target.com -t websocket-flow.yaml\n# error: failed to transform input for protocol websocket\n\n# after\nnuclei -u wss://target.com -t websocket-flow.yaml","handlingStrategy":"validation","validationCode":"// Pre-shape inputs per protocol before the scan (mirrors input.Helper.Transform rules):\nfunc suitableForWebsocket(input string) bool {\n    return strings.HasPrefix(input, \"ws://\") || strings.HasPrefix(input, \"wss://\")\n}\n// file/offline-http requests need path-like inputs:\nfunc suitableForFilepath(input string) bool {\n    return !strings.Contains(input, \"://\") && !regexp.MustCompile(`:\\d+$`).MatchString(input)\n}","typeGuard":"func inputTransformsCleanly(h *input.Helper, in string, t templateTypes.ProtocolType) bool {\n    return h.Transform(in, t) != \"\"\n}","tryCatchPattern":"// The flow logs via ctx.LogError and aborts; watch the callback:\nctx.OnResult = func(e *output.ResultEvent) { ... }\n// after Execute, ctx.Errors()/log will contain 'failed to transform input' — map it to a\n// user-facing hint: 'provide ws://-prefixed targets for websocket flow templates'","preventionTips":["Maintain separate target lists per scheme (http URLs vs ws/wss vs file paths)","For mixed-protocol detection, split into multiple single-protocol templates","Pre-check inputs with the same Transform rules when using the SDK InputHelper"],"tags":["flow","input","transform","websocket","targets"],"backgroundTag":null,"analyzedSha":"265b3a3dec374741614e342f813c10f8b38d2bb7","analyzedAt":"2026-08-15T20:05:51.855Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}