Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Common Errors

This document provides solutions for the most frequently encountered Fluree errors.

LEDGER_NOT_FOUND

{
  "error": "NotFound",
  "message": "Ledger not found: mydb:main",
  "code": "LEDGER_NOT_FOUND"
}

Causes

  1. Ledger doesn’t exist
  2. Typo in ledger name
  3. Wrong branch name
  4. Nameservice not initialized

Solutions

Check ledger exists:

curl http://localhost:8090/v1/fluree/ledgers

Create ledger:

curl -X POST "http://localhost:8090/v1/fluree/create" \
  -H "Content-Type: application/json" \
  -d '{"ledger": "mydb:main"}'

Verify spelling:

  • Check for typos in ledger name
  • Verify branch name (default is main)
  • Check case sensitivity

PARSE_ERROR

{
  "error": "ParseError",
  "message": "Invalid JSON-LD: unexpected token at line 5",
  "code": "PARSE_ERROR",
  "details": {
    "line": 5,
    "column": 12
  }
}

Causes

  1. Invalid JSON syntax
  2. Invalid JSON-LD structure
  3. Invalid SPARQL syntax
  4. Missing required fields

Solutions

Validate JSON:

# Use jq to validate
cat query.json | jq .

Check JSON-LD:

  • Validate @context format
  • Check @id and @type values
  • Verify array vs object usage

Check SPARQL:

  • Validate syntax online
  • Check PREFIX declarations
  • Verify quote matching

Common JSON Mistakes:

// Bad: trailing comma
{
  "select": ["?name"],
  "where": [...],
}

// Good: no trailing comma
{
  "select": ["?name"],
  "where": [...]
}

INVALID_IRI

{
  "error": "ValidationError",
  "message": "Invalid IRI: not a valid URI",
  "code": "INVALID_IRI",
  "details": {
    "iri": "not a uri"
  }
}

Causes

  1. Malformed IRI
  2. Missing namespace prefix
  3. Invalid characters
  4. Spaces in IRI

Solutions

Use valid IRIs:

// Good
{"@id": "http://example.org/alice"}
{"@id": "ex:alice"}

// Bad
{"@id": "not a uri"}
{"@id": "alice"}  // Missing namespace
{"@id": "ex:alice smith"}  // Space

Define namespace:

{
  "@context": {
    "ex": "http://example.org/ns/"
  },
  "@graph": [
    {"@id": "ex:alice"}  // Now valid
  ]
}

URL encode spaces:

{"@id": "ex:alice%20smith"}

UNRESOLVED_COMPACT_IRI

Unresolved compact IRI 'ex:Person': prefix 'ex' is not defined in @context.
If this is intended as an absolute IRI, use a full form (e.g. http://...)
or add the prefix to @context.

This error fires from the JSON-LD strict compact-IRI guard. A value that looks like a compact IRI (prefix:suffix) appeared in an IRI position, but prefix is not defined in @context and is not a recognised absolute scheme.

Causes

  1. Forgotten @context on a query or transaction
  2. Misspelled or missing prefix in @context
  3. Intentionally using a bare prefix:suffix string as an opaque identifier

Solutions

Add the missing prefix to @context (most common fix):

{
  "@context": {"ex": "http://example.org/ns/"},
  "@graph": [{"@id": "ex:alice", "ex:name": "Alice"}]
}

Use a full absolute IRI instead of the compact form:

{
  "@graph": [
    {"@id": "http://example.org/ns/alice", "http://example.org/ns/name": "Alice"}
  ]
}

Opt out of the guard for legacy data where bare prefix:suffix strings are intentional:

{
  "@context": {"ex": "http://example.org/ns/"},
  "opts": {"strictCompactIri": false},
  "@graph": [{"@id": "legacy:alice", "ex:name": "Alice"}]
}

The opt-out applies to both queries and transactions. See IRIs and @context — Strict Compact-IRI Guard for the full policy.

QUERY_TIMEOUT

{
  "error": "Timeout",
  "message": "Query execution exceeded timeout of 30000ms",
  "code": "QUERY_TIMEOUT",
  "details": {
    "timeout_ms": 30000,
    "elapsed_ms": 31245
  }
}

Causes

  1. Complex query
  2. Large result set
  3. High indexing lag
  4. Insufficient resources

Solutions

Add LIMIT:

{
  "select": ["?name"],
  "where": [...],
  "limit": 100  // Add limit
}

Add filters:

{
  "where": [...],
  "filter": "?age > 18"  // Reduce result set
}

Check indexing lag:

curl http://localhost:8090/v1/fluree/info/mydb:main
# If (t - index.t) is large, wait for indexing (or reduce write rate)

Simplify query:

  • Break into smaller queries
  • Remove unnecessary joins
  • Use more specific patterns

Increase timeout: the limit is server-wide (default 15 minutes; 0 disables it), not per request:

FLUREE_QUERY_TIMEOUT_MS=1800000 fluree server run

POLICY_DENIED

{
  "error": "Forbidden",
  "message": "Policy denies access to ledger mydb:main",
  "code": "POLICY_DENIED",
  "details": {
    "subject": "did:key:z6Mkh...",
    "action": "query",
    "ledger": "mydb:main"
  }
}

Causes

  1. No permission for operation
  2. Missing authentication
  3. Policy misconfiguration
  4. Wrong DID/identity

Solutions

Check authentication:

# Are you sending credentials?
curl -H "Authorization: Bearer token" ...

Verify policy:

# Query policies
SELECT ?policy ?subject ?action ?allow
WHERE {
  ?policy a f:Policy .
  ?policy f:subject ?subject .
  ?policy f:action ?action .
  ?policy f:allow ?allow .
}

Check whether policy filtered the request:

curl -X POST http://localhost:8090/v1/fluree/query/mydb \
  -H "fluree-track-policy: true" \
  -H "Authorization: Bearer <token>" \
  -d '{...}'

The response reports which policies ran (policy) and whether policy governed the request at all (policy_enforcement). See Detecting that policy was applied.

Check DID:

  • Verify DID in signed request
  • Check DID is registered
  • Verify public key

TYPE_ERROR

{
  "error": "TypeError",
  "message": "Expected integer, got string",
  "code": "TYPE_ERROR",
  "details": {
    "expected": "xsd:integer",
    "actual": "xsd:string",
    "value": "not a number"
  }
}

Causes

  1. Wrong datatype
  2. Type mismatch in comparison
  3. Invalid type conversion

Solutions

Use correct types:

// Good
{"ex:age": 30}
{"ex:age": {"@value": "30", "@type": "xsd:integer"}}

// Bad
{"ex:age": "30"}  // String, not integer

Check type constraints:

  • Verify expected types
  • Use explicit @type
  • Validate before submitting

PAYLOAD_TOO_LARGE

A 413 carries one of two @type codes — branch on @type, not the status (see API Errors for the full contract):

{
  "error": "request body exceeds the configured limit",
  "status": 413,
  "@type": "err:db/PayloadTooLarge"
}
{
  "error": "Transaction would exceed novelty limit: current=0, delta=2048576, max=1048576",
  "status": 413,
  "@type": "err:db/NoveltyDeltaTooLarge"
}

Causes

  1. err:db/PayloadTooLarge: the raw HTTP request body exceeds the server’s configured body-size limit (refused before parsing)
  2. err:db/NoveltyDeltaTooLarge: the parsed transaction’s novelty delta alone meets or exceeds reindex_max_bytes — no indexer draining can ever admit it, so retrying the same request can never succeed

Solutions

Batch large transactions:

const batchSize = 1000;
for (let i = 0; i < entities.length; i += batchSize) {
  const batch = entities.slice(i, i + batchSize);
  await transact({"@graph": batch});
}

Use LIMIT for queries:

{
  "select": ["?name"],
  "where": [...],
  "limit": 1000  // Paginate
}

Increase limits (if appropriate):

# Raise the request body limit (err:db/PayloadTooLarge); default 50 MB
fluree server run -- --body-limit 104857600

# Raise the novelty hard limit (err:db/NoveltyDeltaTooLarge)
fluree server run -- --reindex-max-bytes 2147483648

DATATYPE_LIMIT_EXCEEDED

{
  "error": "datatype limit exceeded: the ledger holds 16369 of at most 16369 non-reserved datatypes, and this write would add 1 more",
  "status": 422,
  "@type": "err:db/DatatypeLimitExceeded"
}

Causes

The write uses datatypes the ledger has not seen before, and the ledger has no room for them. A ledger holds at most 16,369 distinct datatypes beyond 15 reserved ones. Every datatype counts from its first use, even after its data is retracted. See Datatype Limit for which datatypes count.

The write was refused before anything was committed, so the ledger is unchanged. Retrying the same write cannot succeed.

Solutions

Reuse datatypes the ledger already holds. Values typed with an existing datatype are always accepted.

Move the varying part out of the datatype. Unit and currency vocabularies often use one datatype per unit. Record the unit in its own property instead:

ex:room1 ex:area "42.5"^^xsd:decimal ;
         ex:areaUnit unit:SquareMetre .

STORAGE_ERROR

{
  "error": "StorageError",
  "message": "Cannot write to storage",
  "code": "STORAGE_ERROR"
}

Causes

  1. Disk full (file storage)
  2. Permission errors
  3. AWS connectivity (AWS storage)
  4. Storage backend down

Solutions

File Storage:

# Check disk space
df -h /var/lib/fluree

# Check permissions
ls -la /var/lib/fluree
sudo chown -R fluree:fluree /var/lib/fluree

AWS Storage:

# Check AWS credentials
aws sts get-caller-identity

# Check S3 access
aws s3 ls s3://fluree-prod-data/

# Check DynamoDB
aws dynamodb describe-table --table-name fluree-nameservice

HIGH_INDEXING_LAG

Not an error, but a warning condition.

Symptoms

curl http://localhost:8090/v1/fluree/info/mydb:main
{
  "commit_t": 150,
  "index_t": 0
}

Causes

  1. Transaction rate exceeds indexing capacity
  2. Large transactions
  3. Insufficient resources
  4. Storage bottleneck

Solutions

Tune indexing:

fluree server run -- \
  --indexing-enabled \
  --reindex-min-bytes 100000 \
  --reindex-max-bytes 1000000

Reduce transaction rate:

// Add delay between transactions
await transact(data);
await sleep(100);

Wait for catchup:

async function waitForIndexing() {
  while (true) {
    const status = await getStatus();
    const lag = status.commit_t - status.index_t;
    if (lag < 10) break;
    await sleep(1000);
  }
}

Add resources:

  • More CPU
  • More memory
  • Faster disk

CONCURRENT_MODIFICATION

{
  "error": "Conflict",
  "message": "Concurrent modification detected",
  "code": "CONCURRENT_MODIFICATION"
}

Causes

  1. Multiple processes updating same entity
  2. Nameservice contention
  3. Race condition

Solutions

Implement retry:

async function transactWithRetry(data, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await transact(data);
    } catch (err) {
      if (err.code === 'CONCURRENT_MODIFICATION' && i < maxRetries - 1) {
        await sleep(Math.pow(2, i) * 100);
        continue;
      }
      throw err;
    }
  }
}

Use upsert for retry-friendly transactions:

# Upsert is more retry-friendly for idempotent entity transactions
POST /upsert?ledger=mydb:main

SIGNATURE_VERIFICATION_FAILED

{
  "error": "SignatureVerificationFailed",
  "message": "Invalid signature",
  "code": "INVALID_SIGNATURE"
}

Causes

  1. Wrong private key
  2. Payload modified after signing
  3. Incorrect algorithm
  4. Key not registered

Solutions

Verify signing process:

// Ensure payload not modified
const payload = JSON.stringify(transaction);
const jws = await sign(payload, privateKey);
// Don't modify payload after signing

Check algorithm:

{
  "alg": "EdDSA",  // Must match key type
  "kid": "did:key:z6Mkh..."
}

Verify public-key material: standalone server signed requests use the key material embedded in supported JWS/JWT headers (or configured OIDC JWKS). There is no /admin/keys registration endpoint.

Memory Issues

Symptoms

  • Out of memory errors
  • Server crashes
  • Slow performance
  • Swap usage

Solutions

Check memory:

curl http://localhost:8090/v1/fluree/stats

Reduce memory usage:

# See docs/operations/configuration.md for current memory-related flags.
# In general: reduce write/query load, reduce indexing lag, and provision more RAM.

Add more RAM:

  • Upgrade server
  • Use cloud instance with more memory

Reduce novelty:

  • Index more frequently
  • Reduce transaction size

Troubleshooting Checklist

When encountering issues, check:

  1. Server is running
  2. Can connect to server
  3. Health endpoint returns healthy
  4. Logs show no errors
  5. Ledger exists
  6. Correct ledger name/branch
  7. Valid JSON-LD/SPARQL syntax
  8. Sufficient resources (disk, memory)
  9. No network issues
  10. Authentication working (if required)