Skip to main content
This page covers every constraint enforced by the Facets API, organized by the problem you’re trying to solve. If your request failed or something behaved unexpectedly, find your scenario below.

My content type won’t create

You’re calling define_content_type and getting an error.
What you triedWhy it failedError
Used uppercase, spaces, or underscores in codeCodes must be lowercase alphanumeric with hyphens only: ^[a-z0-9]+(?:-[a-z0-9]+)*$. Examples: nda, service-agreement.422
Created a child with the same code as an existing siblingCodes must be unique among siblings (same parent).409
Created a root with a code that already existsRoot codes must be unique per company.409
Created a path that already exists (e.g. contract:nda)Full paths must be unique per company.409
Added a 5th level to the treeTrees can be at most 4 levels deep.400
Path exceeds 768 charactersMaximum path length is 768 characters.400
Good to know:
  • A company can have multiple independent trees (different roots). They don’t interact.
  • Content types are company-scoped, not workspace-scoped.
  • You cannot reparent a node. To move it, delete it with undefine_content_type and recreate it under the new parent. Deleting cascades: see What happens when I delete something? below.

My attribute won’t save

You’re calling define_attribute and getting an error.
What you triedWhy it failedError
Changed an attribute’s type (e.g. textnumber)attribute_type is immutable once created. Delete the attribute and recreate it.400
Used a name that already exists on this content typeAttribute names must be unique within a content type.409
Used a name that exists elsewhere in the same treeNames can’t conflict across the tree. This prevents ambiguity when attributes are inherited.400
Used a reserved name like content_type, status, or pageSome names are reserved by the system.400
Created a select or multi-select without choicesThese types require a non-empty choices array.400
Good to know:
  • If you omit label, it’s auto-generated from name: fiscal_year becomes "Fiscal Year".
  • 7 attribute types are available: text, number, date, boolean, select, multi-select, rich-text.

My classification was rejected

You’re calling classify on a file and getting an error.
What you triedWhy it failedError
Classified a file that’s already classified in the same tree under a sibling path (e.g. contract:ndacontract:service-agreement)A file can only have one classification per tree. You can’t jump between siblings. Unclassify first, then classify the new path.400
Called set_value on a file that isn’t classified yetYou must classify a file before setting any attribute values.400

Reclassification: what’s allowed

When a file is already classified and you call classify with a different path in the same tree, the result depends on the relationship:
You’re moving to…What happens
The same pathNothing changes. Idempotent, returns 200.
A descendant (e.g. contractcontract:nda)Auto-migrates. All attribute values are preserved.
An ancestor (e.g. contract:ndacontract)Auto-migrates. Child-specific values (like is_mutual) are deleted because they don’t exist on the ancestor.
A sibling (e.g. contract:ndacontract:service-agreement)Rejected. Unclassify first, then classify the new path.
A file can be classified under multiple trees at the same time (e.g. one contract:nda classification and one regulation:gdpr classification).

My value was rejected

You’re calling set_value and getting a 422.
Attribute typeWhat’s acceptedWhat’s rejected
text, rich-textStringAnything else
numberInteger, float, or numeric string (50000, "1.5")Booleans, non-numeric strings
dateISO 8601 string, normalized to YYYY-MM-DDNon-date strings
booleantrue or false (real booleans)"true", 1, "yes"
selectSingle string matching a defined choiceString not in choices
multi-selectList of strings, each matching a defined choiceSingle string, values not in choices
To remove a value, use clear_value. Passing null to set_value is rejected with 422.

What happens when I delete something?

Deleting things in Facets cascades. Here’s exactly what gets removed:
You delete…What’s also removed
A content type (undefine_content_type)Its entire subtree, all file classifications referencing those paths, and all their attribute values.
A classification (unclassify)All attribute values stored under that classification on that file.
An attribute (undefine_attribute)All stored values for that attribute across every file.
Choices from a select/multi-selectAny stored values that referenced the removed choices are deleted across all files.
All deletions are irreversible.

What can I filter on?

Not all attribute types support filtering via GET /api/v3/files?attribute=.
TypeFilterable?Why
textYesIndexed in value_text
numberYesIndexed in value_number
dateYesIndexed in value_date
booleanYesIndexed in value_boolean
selectYesIndexed in value_text
multi-selectNoNot indexed. Use select if you need filtering
rich-textNoNot indexed. Use text if you need filtering

Multi-select containment

When filtering on a multi-select attribute, the filter uses containment: jurisdiction:FR matches files with ["FR", "DE"], ["FR"], or any list containing "FR".

Date shortcuts and ambiguity

LightOn expands partial dates into ranges:
  • effective_date:2025 → any date in 2025 (Jan 1 – Dec 31)
  • effective_date:2025-03 → any date in March 2025
  • effective_date:2025-03-01 → exact date
A 4-digit value like 2025 is ambiguous: it could be a year, a number, or text. LightOn searches across all typed columns with an OR. Use full YYYY-MM-DD format for exact date matches.

How does inheritance work?

Attribute inheritance is resolved at query time, not stored. When you read a file’s facets, LightOn walks up the tree and collects attributes from each ancestor with inherit_attributes: true.
ScenarioWhat happens
You add an attribute to a parent nodeImmediately available on all descendants. No migration needed.
You set inherit_attributes: false on a nodeBreaks the chain. That node and its descendants won’t inherit attributes from above.
A child defines an attribute with the same name as an inherited oneThe child’s definition shadows the parent’s. The child’s type, choices, and required flag take precedence.

Batch behavior

When you send a JSON array of actions, LightOn processes them in order and fails fast on the first error:
  • Actions before the failure are committed.
  • Actions after the failure are skipped.
  • BM25 reindex is scheduled once per batch, not per action.
All schema actions (adopt, define_content_type, define_attribute, undefine_*) are idempotent and safe to replay.