siyuan-note/siyuan · error

unsupported embedded request field

Error message

unsupported embedded request field: %s

What it means

decodeRequestFields walks the request struct's fields; anonymous (embedded) fields with no JSON name must be structs so they can be recursed into. An embedded non-struct (e.g. an embedded string, int, or interface) cannot be flattened, so this error names the offending field. It indicates an unsupported contract shape.

Solutions

  1. Give the embedded field an explicit json name instead of embedding it
  2. Change the embedded type to a struct, or wrap the primitive in a named field
  3. Restructure the request contract so all anonymous fields are structs

Example fix

// before
type Req struct {
	Base
	string // anonymous non-struct embed
}
// after
type Req struct {
	Base
	Note string `json:"note"`
}
Defensive patterns

Strategy: validation

Validate before calling

func validateContractShape(v reflect.Value) error { t := v.Type(); for i := 0; i < t.NumField(); i++ { f := t.Field(i); if f.Anonymous && strings.Split(f.Tag.Get("json"), ",")[0] == "" && f.Type.Kind() != reflect.Struct { return fmt.Errorf("embedded field %s must be a struct", f.Name) } }; return nil }

Type guard

func embeddable(f reflect.StructField) bool { return !f.Anonymous || strings.Split(f.Tag.Get("json"), ",")[0] != "" || f.Type.Kind() == reflect.Struct }

Try / catch

if err := decodeRequestFields(value, fields); err != nil { if strings.HasPrefix(err.Error(), "unsupported embedded request field") { return fmt.Errorf("fix contract struct shape: %w", err) }; return err }

Prevention

When it happens

Trigger: Embedding a non-struct type (embedded string, embedded interface, embedded map) in a request contract struct without a json tag name.

Common situations: Embedding a type alias like `type ID string` directly; embedding time.Duration or another named primitive; auto-generating contracts from types with primitive embeds.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/5438178f1854f1a8. Report an issue: GitHub.

Appendix: source

Thrown at kernel/apicontract/decode.go:59

	}
	value := reflect.ValueOf(&request).Elem()
	if value.Kind() != reflect.Struct {
		return request, fmt.Errorf("request contract must be a struct")
	}
	err = decodeRequestFields(value, fields)
	return request, err
}

func decodeRequestFields(value reflect.Value, fields map[string]json.RawMessage) error {
	for i := 0; i < value.NumField(); i++ {
		field := value.Type().Field(i)
		name := strings.Split(field.Tag.Get("json"), ",")[0]
		if (!field.IsExported() && !field.Anonymous) || name == "-" {
			continue
		}
		if field.Anonymous && name == "" {
			if field.Type.Kind() != reflect.Struct {
				return fmt.Errorf("unsupported embedded request field: %s", field.Name)
			}
			if err := decodeRequestFields(value.Field(i), fields); err != nil {
				return err
			}
			continue
		}
		options := "," + field.Tag.Get("api") + ","
		has := func(option string) bool { return strings.Contains(options, ","+option+",") }
		raw, present := fields[name]
		if !present {
			if has("optional") {
				continue
			}
			return fmt.Errorf("Field [%s] is required", name)
		}
		isNull := bytes.Equal(bytes.TrimSpace(raw), []byte("null"))
		if isNull && !has("nullable") && field.Type.Kind() != reflect.Pointer && !has("filterstrings") {
			return fmt.Errorf("Field [%s] must not be null", name)

View on GitHub (pinned to 9f775e8a12)