fatedier/frp · error

field "%s": cannot unmarshal %s into %s

Error message

field "%s": cannot unmarshal %s into %s

What it means

The line-less variant of the type-mismatch message: the same JSON UnmarshalTypeError reached the formatter but includeLine was false (or the field could not be located in the content), so only field path, offending value, and target type are reported. It means a config field has the wrong value type, without positional info.

Source

Thrown at pkg/config/load.go:243

	}
	var strictErr *toml.StrictMissingError
	if errors.As(err, &strictErr) {
		return strictErr
	}
	return err
}

// enhanceDecodeError tries to add field path and line number information to JSON/YAML decode errors.
func enhanceDecodeError(err error, originalContent []byte, includeLine bool) error {
	var typeErr *json.UnmarshalTypeError
	if errors.As(err, &typeErr) && typeErr.Field != "" {
		if includeLine {
			line := findFieldLineInContent(originalContent, typeErr.Field)
			if line > 0 {
				return fmt.Errorf("line %d: field \"%s\": cannot unmarshal %s into %s", line, typeErr.Field, typeErr.Value, typeErr.Type)
			}
		}
		return fmt.Errorf("field \"%s\": cannot unmarshal %s into %s", typeErr.Field, typeErr.Value, typeErr.Type)
	}
	return err
}

// findFieldLineInContent searches the original config content for a field name
// and returns the 1-indexed line number where it appears, or 0 if not found.
func findFieldLineInContent(content []byte, fieldPath string) int {
	if fieldPath == "" {
		return 0
	}

	// Use the last component of the field path (e.g. "proxies" from "proxies" or
	// "protocol" from "transport.protocol").
	parts := strings.Split(fieldPath, ".")
	searchKey := parts[len(parts)-1]

	lines := bytes.Split(content, []byte("\n"))
	for i, line := range lines {

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Use the field path in the message to locate the value manually and fix its type
  2. If available, load the same content from a file so the line-number variant pinpoints the location
  3. Validate generated configs against frp's JSON schema before shipping

Example fix

// before
{ "transport": { "heartbeatInterval": "30" } }

// after
{ "transport": { "heartbeatInterval": 30 } }
Defensive patterns

Strategy: validation

Validate before calling

// when assembling config programmatically, marshal+unmarshal through the typed struct first
probe := v1.ClientConfig{}
if err := json.Unmarshal(assembled, &probe); err != nil { var te *json.UnmarshalTypeError; if errors.As(err, &te) { return fmt.Errorf("field %s wrong type", te.Field) } }

Try / catch

if err := json.Unmarshal(data, &cfg); err != nil { var te *json.UnmarshalTypeError; if errors.As(err, &te) && te.Field != "" { /* report field path to caller for manual location */ } }

Prevention

When it happens

Trigger: Decoding frp config from sources where line mapping is not attempted — e.g. content assembled programmatically or strict-mode YAML paths that skip line lookup — and a field's value cannot be converted (string into int, object into array, etc.).

Common situations: Configs generated by tools/Helm templates that emit quoted numbers or wrong nesting; API-driven config assembly where the operator cannot rely on line numbers anyway.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/11c30fcdfef144cd. Report an issue: GitHub.