cayleygraph/cayley · error

unsupported type for id field: %T

Error message

unsupported type for id field: %T

What it means

When computing a node ID for a struct instance, the idFor machinery accepts quad.IRI, quad.BNode, and string values for the id field. Any other Go type stored in the id field cannot be converted to a node identifier, so this error is returned. It propagates out of configFrom/open when loading a schema config that specifies an id field of an unsupported type.

Source

Thrown at schema/schema.go:450

	return rv.Interface() == reflect.Zero(rv.Type()).Interface()
}

func (c *Config) idFor(rules fieldRules, rt reflect.Type, rv reflect.Value, pref string) (id quad.Value, err error) {
	hasAnon := false
	for i := 0; i < rt.NumField(); i++ {
		fld := rt.Field(i)
		hasAnon = hasAnon || fld.Anonymous
		if _, ok := rules[pref+fld.Name].(idRule); ok {
			vid := rv.Field(i).Interface()
			switch vid := vid.(type) {
			case quad.IRI:
				id = c.iri(vid)
			case quad.BNode:
				id = vid
			case string:
				id = c.toIRI(vid)
			default:
				err = fmt.Errorf("unsupported type for id field: %T", vid)
			}
			return
		}
	}
	if !hasAnon {
		return
	}
	// second pass - look for anonymous fields
	for i := 0; i < rt.NumField(); i++ {
		fld := rt.Field(i)
		if !fld.Anonymous {
			continue
		}
		id, err = c.idFor(rules, fld.Type, rv.Field(i), pref+fld.Name+".")
		if err != nil || id != nil {
			return
		}
	}

View on GitHub (pinned to 81dcd7d73e)

Solutions

  1. Change the id field to a string (or quad.IRI/quad.BNode) type
  2. Convert the value to string before it reaches schema ID generation (e.g. strconv.FormatInt)
  3. Implement a custom ID method (ID() quad.IRI or similar supported hook) on the struct

Example fix

// before
type Doc struct {
    ID   int64 `quad:"@id"`
}
// after
type Doc struct {
    ID   string `quad:"@id"`
}
Defensive patterns

Strategy: type-guard

Validate before calling

switch id := doc.ID.(type) {
case string, quad.IRI, quad.BNode:
    // ok
default:
    return fmt.Errorf("id field must be string/IRI/BNode, got %T", id)
}

Type guard

func validID(v interface{}) bool {
    switch v.(type) {
    case string, quad.IRI, quad.BNode:
        return true
    }
    return false
}

Try / catch

if _, err := w.WriteAsQuads(qs, doc); err != nil {
    if strings.Contains(err.Error(), "unsupported type for id field") {
        return fmt.Errorf("convert doc.ID to string before writing: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Configuring an id field (via Config or the appengine configFrom path) whose runtime value is, e.g., an int, float, struct, or other non-string/non-quad type; idFor then hits the default branch and fails.

Common situations: Using int or int64 auto-increment IDs in structs mapped with an id tag; loading a JSON schema config whose id type doesn't match what the loader expects.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of cayleygraph/cayley@81dcd7d73e (2026-09-06). Data as JSON: /api/errors/9d0250339f84f0e8. Report an issue: GitHub.