cayleygraph/cayley · error
can not parse JSON-LD identifier: %#v
Error message
can not parse JSON-LD identifier: %#v
What it means
parseIdentifier is the last resort when converting a JSON-LD identifier string: it first tries parseBNode ("_:"-prefixed blank nodes) and then parseIRI (any other string is accepted as an IRI), so in practice this error fires when the input is not a string at all, or when the fallback path is handed an empty/invalid value. It signals that the value cannot be interpreted as any JSON-LD identifier form the registry supports.
Source
Thrown at query/linkedql/registry.go:263
return "", fmt.Errorf("blank node ID must start with \"_:\"")
}
return quad.BNode(s[2:]), nil
}
func parseIRI(s string) (quad.IRI, error) {
return quad.IRI(s), nil
}
func parseIdentifier(s string) (quad.Value, error) {
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), nilView on GitHub (pinned to 81dcd7d73e)
Solutions
- Ensure the identifier is a string: either an absolute IRI ("http://...") or a blank node ("_:label").
- If the value is a node object, wrap it as {"@id": "<iri-or-bnode>"} so parseIdentifierString extracts the id first.
- Convert non-string scalars explicitly (fmt.Sprintf or a serializer) before placing them in identifier positions.
- Log %#v of the offending value and trace which step field supplied it.
Example fix
// before
{"@id": 42}
// after
{"@id": "http://example.com/entity/42"} Defensive patterns
Strategy: type-guard
Validate before calling
func isUsableIdentifier(v interface{}) bool {
switch t := v.(type) {
case string:
return t != "" && (strings.HasPrefix(t, "_:") || strings.Contains(t, ":"))
case map[string]interface{}:
_, ok := t["@id"].(string)
return ok
}
return false
} Type guard
func asIdentifier(v interface{}) (string, bool) {
switch t := v.(type) {
case string:
return t, true
case map[string]interface{}:
id, ok := t["@id"].(string)
return id, ok
}
return "", false
} Try / catch
id, err := linkedql.BuildIdentifier(raw)
if err != nil {
if strings.Contains(err.Error(), "can not parse JSON-LD identifier") {
log.Printf("bad identifier %#[1]v in step field", raw)
return fmt.Errorf("identifier field requires IRI or _:bnode string, got %T", raw)
}
return err
} Prevention
- Validate that identifier fields contain only strings or {"@id": string} maps before submission.
- Avoid interpolating raw scalars (numbers, bools) into @id positions.
- Share one identifier-construction helper across all step-building code.
When it happens
Trigger: BuildIdentifier or parseValue receiving a value that is neither a map with "@id" nor a usable string — e.g. a number, bool, nil, or array passed where an identifier (string or {"@id": ...}) is expected in a step document.
Common situations: Programmatic construction of LinkedQL steps where a variable of the wrong type is interpolated into an @id field; JSON decoding quirks where numbers or nulls end up in identifier positions; templating errors that render empty strings.
Related errors
- blank node ID must start with "_:"
- unexpected type: %T
- expected a @id key
- 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/488c999f368dacdc.
Report an issue: GitHub.