cayleygraph/cayley · error
expected a @id key
Error message
expected a @id key
What it means
After confirming the input is a map, parseIdentifierString reads m["@id"] and asserts it is a string. If the map has no "@id" key — or "@id" holds a non-string value like a number or nested object — this error is returned. JSON-LD node objects used as identifiers must carry a string "@id" member.
Source
Thrown at query/linkedql/registry.go:273
bnode, err := parseBNode(s)
if err == nil {
return bnode, nil
}
iri, err := parseIRI(s)
if err == nil {
return iri, nil
}
return nil, fmt.Errorf("can not parse JSON-LD identifier: %#v", s)
}
func parseIdentifierString(a interface{}) (string, error) {
m, ok := a.(map[string]interface{})
if !ok {
return "", fmt.Errorf("unexpected type: %T", a)
}
id, ok := m["@id"].(string)
if !ok {
return "", fmt.Errorf("expected a @id key")
}
return id, nil
}
func parseLiteral(a interface{}) (quad.Value, error) {
switch a := a.(type) {
case string:
return quad.String(a), nil
case int64:
return quad.Int(a), nil
case float64:
i := int64(a)
if a == float64(i) {
return quad.Int(i), nil
}
return quad.Float(a), nil
case bool:
return quad.Bool(a), nilView on GitHub (pinned to 81dcd7d73e)
Solutions
- Add a string "@id" key to the node object: {"@id": "http://example.com/x"}.
- Fix the key spelling/casing — it must be exactly "@id".
- If the object is a literal, ensure it flows to a literal-accepting field (parseLiteral) rather than an identifier field.
- Ensure "@id"'s value is a plain JSON string, not a number or nested object.
Example fix
// before
{"type": "Person", "name": "Alice"}
// after
{"@id": "http://example.com/people/alice", "type": "Person", "name": "Alice"} Defensive patterns
Strategy: validation
Validate before calling
func hasStringID(m map[string]interface{}) bool {
id, ok := m["@id"]
if !ok { return false }
s, ok := id.(string)
return ok && s != ""
}
// if !hasStringID(node) { return errors.New("node object requires a string @id") } Type guard
func extractID(v interface{}) (string, bool) {
m, ok := v.(map[string]interface{})
if !ok { return "", false }
s, ok := m["@id"].(string)
return s, ok && s != ""
} Try / catch
id, err := parseIdentifierString(node)
if err != nil && err.Error() == "expected a @id key" {
return fmt.Errorf("node %v is a value/literal object, not an identifier: add a string @id", node)
} Prevention
- Check for the exact key "@id" — watch for "id"/"ID" typos.
- Keep literal value objects ({"@value": ...}) out of identifier positions.
- Validate step documents with a JSON-LD-aware linter before execution.
When it happens
Trigger: Passing a node object without "@id" (e.g. {"@value": "x"} literal form, or {"type": "Person"}) or with a non-string "@id" (e.g. {"@id": 123}) into parseValue/BuildIdentifier paths.
Common situations: Using JSON-LD value objects ({"@value"/"@language"/"@type"}) where identifiers are required; documents produced by other JSON-LD tooling that emit @id as a nested structure; typos like "ID" or "id" instead of "@id".
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
- blank node ID must start with "_:"
- can not parse JSON-LD identifier: %#v
- unexpected type: %T
- can not parse %#v as a literal
- cannot count iterator without a valid context
AI-assisted analysis of cayleygraph/cayley@81dcd7d73e (2026-09-06).
Data as JSON: /api/errors/9952068066be0827.
Report an issue: GitHub.