cloudflare/cloudflared · error

couldn't parse index from timeout hop: %w

Error message

couldn't parse index from timeout hop: %w

What it means

DecodeLine (Unix) parses traceroute output lines for timeout hops, expecting the first whitespace-separated field to be the hop index. When strconv.ParseUint cannot parse parts[0] as an integer, the line does not follow the expected traceroute format and this wrapped error is returned, failing network diagnostic decoding.

Source

Thrown at diagnostic/network/collector_unix.go:53

	process := exec.CommandContext(ctx, command, args...)

	return decodeNetworkOutputToFile(process, DecodeLine)
}

func DecodeLine(text string) (*Hop, error) {
	fields := strings.Fields(text)
	parts := []string{}
	filter := func(s string) bool { return s != "*" && s != "ms" }

	for _, field := range fields {
		if filter(field) {
			parts = append(parts, field)
		}
	}

	index, err := strconv.ParseUint(parts[0], 10, 8)
	if err != nil {
		return nil, fmt.Errorf("couldn't parse index from timeout hop: %w", err)
	}

	if len(parts) == 1 {
		return NewTimeoutHop(uint8(index)), nil
	}

	domain := ""
	rtts := []time.Duration{}

	for _, part := range parts[1:] {
		rtt, err := strconv.ParseFloat(part, 64)
		if err != nil {
			domain += part + " "
		} else {
			rtts = append(rtts, time.Duration(rtt*MicrosecondsFactor))
		}
	}

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Ensure the standard iputils traceroute is installed and used (check which traceroute).
  2. Verify traceroute output manually for the target host and confirm hop lines parse as expected.
  3. Fix locale-related formatting (LC_ALL=C) if numeric output is localized.
  4. Handle the error in the caller by skipping/reporting unparseable lines rather than treating the whole diagnostic as fatal, if a traceroute variant must be tolerated.

Example fix

// before
hops, _, err := network.Collect(ctx, config)
if err != nil {
	return err
}
// after
hops, _, err := network.Collect(ctx, config)
if err != nil {
	var parseErr *strconv.NumError
	if errors.As(err, &parseErr) {
		// non-numeric hop index: incompatible traceroute output
		log.Warn().Err(err).Msg("unexpected traceroute format")
	}
	return fmt.Errorf("network collect: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

// pre-check the traceroute binary and a sample of its output format
out, err := exec.Command("traceroute", "--help").Output()
if err != nil {
	// unsupported or missing traceroute
}

Try / catch

hops, err := network.Collect(ctx, cfg)
var numErr *strconv.NumError
if errors.As(err, &numErr) {
	// incompatible traceroute output: log raw tee output for debugging
}

Prevention

When it happens

Trigger: Decoding a traceroute line whose first token is not a number — e.g. a header line like 'traceroute to ...', warning lines, or output from a traceroute variant with a different column layout piped into Decode.

Common situations: Running an unsupported traceroute implementation (busybox, musl variants) whose output format differs; traceroute emits non-hop lines (DNS warnings, headers) that reach the timeout-hop branch; locale changes the output format.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/207dcc2c68a656f4. Report an issue: GitHub.