projectdiscovery/nuclei · error

failed to transform input for protocol %s

Error message

failed to transform input for protocol %s

What it means

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).

Source

Thrown at pkg/tmplexec/flow/flow_internal.go:40

		evaluation := f.options.Variables.EvaluateScope(scope)
		f.options.GetTemplateCtx(f.ctx.Input.MetaInput).Merge(evaluation.Values) // merge all variables into template context
		f.options.GetTemplateCtx(f.ctx.Input.MetaInput).MergeTemplateVariables(evaluation.TemplateValues)

		// to avoid polling update template variables everytime we execute a protocol
		m := f.options.GetTemplateCtx(f.ctx.Input.MetaInput).GetAll()
		_ = runtime.Set("template", m)
	}()
	matcherStatus := &atomic.Bool{} // due to interactsh matcher polling logic this needs to be atomic bool
	// if no id is passed execute all requests in sequence
	if len(opts.reqIDS) == 0 {
		// execution logic for http()/dns() etc
		for index := range f.allProtocols[opts.protoName] {
			req := f.allProtocols[opts.protoName][index]
			// transform input if required
			inputItem := f.ctx.Input.Clone()
			if f.options.InputHelper != nil && f.ctx.Input.MetaInput.Input != "" {
				if inputItem.MetaInput.Input = f.options.InputHelper.Transform(inputItem.MetaInput.Input, req.Type()); inputItem.MetaInput.Input == "" {
					f.ctx.LogError(fmt.Errorf("failed to transform input for protocol %s", req.Type()))
					return false
				}
			}
			err := req.ExecuteWithResults(inputItem, output.InternalEvent(f.options.GetTemplateCtx(f.ctx.Input.MetaInput).GetAll()), output.InternalEvent{}, f.protocolResultCallback(req, matcherStatus, opts))
			if err != nil {
				// save all errors in a map with id as key
				// its less likely that there will be race condition but just in case
				id := req.GetID()
				if id == "" {
					id, _ = reqMap.GetKeyWithValue(req)
				}
				err = f.allErrs.Set(opts.protoName+":"+id, err)
				if err != nil {
					f.ctx.LogError(fmt.Errorf("failed to store flow runtime errors got %v", err))
				}
				return matcherStatus.Load()
			}
		}

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Provide scheme-correct inputs: `ws://target.com` or `wss://target.com` for websocket flow templates
  2. Split the template so each protocol gets inputs it can transform (separate websocket template from http template)
  3. If the input must stay a bare host, remove the websocket/file section from the flow or preprocess inputs to add the scheme
  4. Verify with one host first: `nuclei -u ws://target.com -t flow-template.yaml`

Example fix

# before
nuclei -u target.com -t websocket-flow.yaml
# error: failed to transform input for protocol websocket

# after
nuclei -u wss://target.com -t websocket-flow.yaml
Defensive patterns

Strategy: validation

Validate before calling

// Pre-shape inputs per protocol before the scan (mirrors input.Helper.Transform rules):
func suitableForWebsocket(input string) bool {
    return strings.HasPrefix(input, "ws://") || strings.HasPrefix(input, "wss://")
}
// file/offline-http requests need path-like inputs:
func suitableForFilepath(input string) bool {
    return !strings.Contains(input, "://") && !regexp.MustCompile(`:\d+$`).MatchString(input)
}

Type guard

func inputTransformsCleanly(h *input.Helper, in string, t templateTypes.ProtocolType) bool {
    return h.Transform(in, t) != ""
}

Try / catch

// The flow logs via ctx.LogError and aborts; watch the callback:
ctx.OnResult = func(e *output.ResultEvent) { ... }
// after Execute, ctx.Errors()/log will contain 'failed to transform input' — map it to a
// user-facing hint: 'provide ws://-prefixed targets for websocket flow templates'

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15). Data as JSON: /api/errors/52f10564b154318a. Report an issue: GitHub.