Skip to content

Latest commit

 

History

History
193 lines (140 loc) · 7.41 KB

File metadata and controls

193 lines (140 loc) · 7.41 KB

Troubleshooting

Common symptoms, their cause, and the fix. Each entry shows the error (or the surprising behaviour) you actually see, then what to change.

If your problem is a platform constraint rather than a mistake (macOS extension loading, the ~100k brute-force ceiling, FTS5 availability), see Known Limitations instead.

AttributeError: 'tuple' object has no attribute 'keys' on read

Symptom. Writes succeed, but the first get_thought / search call raises:

AttributeError: 'tuple' object has no attribute 'keys'

Cause. The aiosqlite connection has no row factory, so rows come back as plain tuples. Engrava maps rows to records by column name and needs aiosqlite.Row. The failure surfaces on read, not on connect or write, which makes it look unrelated to setup.

Fix. Set the row factory immediately after connecting:

import aiosqlite

conn = await aiosqlite.connect("engrava.db")
conn.row_factory = aiosqlite.Row  # required

SqliteEngravaCore.from_config(...) opens the connection for you and sets this correctly — the manual snippet above only applies when you construct the store from your own connection.

ValueError: '...' is not a valid ThoughtType (or Priority, EdgeType, …)

Symptom.

ValueError: 'INSIGHT' is not a valid ThoughtType

Cause. A string was passed that is not a member of the enum. The valid ThoughtType members are TASK, OBSERVATION, BELIEF, REFLECTION, OUTPUT_DRAFT, and NOTE — there is no INSIGHT. The same applies to Priority (P1P4), EdgeType, LifecycleStatus, etc.

Fix. Use a real enum member, ideally the symbol rather than a string literal:

from engrava import ThoughtType

ThoughtType.BELIEF  # preferred
ThoughtType("BELIEF")  # also valid — must match a real member

See Core Concepts for the full taxonomy and when to use each type.

Search returns nothing (or fewer results than expected)

Symptom. search_hybrid / search_fts returns an empty or short result list even though matching thoughts exist.

Cause. A signal you assumed was active was silently skipped, so the query ran on fewer signals than you expected. Engrava skips a signal rather than erroring when its prerequisite is missing. Work through this checklist:

If… then…
No embedding_provider is configured the vector signal is skipped — only FTS/priority run. A purely semantic query with no shared keywords may find nothing.
You pass query_text but no provider and no query_vector same as above — there is no vector to compare against.
current_cycle is None the recency signal is skipped (it cannot compute an age).
recency_weight is 0.0 recency is disabled even if current_cycle is set.
The query shares no FTS tokens with any thought FTS legitimately returns nothing — this is a real miss, not a bug.

Inspect which signals actually ran via HybridSearchResult.backends_used:

result = await store.search_hybrid("python async", top_k=5, current_cycle=10)
print(sorted(result.backends_used))  # e.g. ['fts5', 'priority', 'recency']

If 'vector' is missing and you expected semantic matching, configure an embedding provider (see the Embeddings guide). If 'recency' is missing, pass a non-None current_cycle and a recency_weight > 0.

Dreaming promotes nothing (consolidation is inert)

Symptom. run_consolidation(...) returns promoted_count == 0 every time.

Cause. Promotion requires a candidate to clear two independent bars, and either one alone keeps the count at zero:

  1. The age gate. A thought is eligible only when current_cycle - created_cycle >= min_age_cycles (default 1). If you never advance your cycle counter — every thought stays at the same current_cycle you created it in — 0 >= 1 is false and nothing is ever eligible. This is the most common cause. See Core Concepts → Cycle.
  2. The promotion threshold. Even after the gate passes, a candidate's weighted signal score must reach promote_threshold. Brand-new, unconfirmed, never-accessed thoughts score low, so a high threshold promotes nothing.

Fix.

from engrava.config import DreamingConfig, DreamingGates
from engrava.extensions.dreaming import DreamingExtension

config = DreamingConfig(
    enabled=True,
    promote_threshold=0.4,  # lower it if nothing clears the bar
    gates=DreamingGates(
        allow_zero_confirmation=True,  # essential for single-write ingest
        min_age_cycles=1,
    ),
)
ext = DreamingExtension(config=config)

# Advance current_cycle past the thoughts' created_cycle so the age gate passes:
result = await ext.run_consolidation(store, current_cycle=10)
print(result.promoted_count)

See Dreaming for the full gate-and-signal model.

EmbeddingModelMismatchError when opening an existing database

Symptom. A store that worked before now raises EmbeddingModelMismatchError on startup or first embed.

Cause. Engrava records the embedding model name and dimension in the database the first time it embeds. If you later open that same database with a different model name or a different dimension, the stored vectors are incompatible with new ones, so it refuses rather than silently mixing dimensions (which would corrupt similarity results).

Fix. Use the same embedding model the database was created with, or re-embed the corpus under the new model. The CLI does this safely:

engrava restore --re-embed   # validates model consistency, re-embeds

See Known Limitations → Embedding Dimension Consistency.

ReferentialIntegrityError — and you can't import it from engrava

Symptom. Creating an edge to a thought that doesn't exist raises:

referential integrity violation: edge.to_thought_id='...' does not reference an existing thought

…and the obvious import fails:

from engrava import ReferentialIntegrityError  # ImportError!

Cause (two parts).

  1. The error itself means one endpoint of an edge (from_thought_id or to_thought_id) is not a real thought id. Create both thoughts before the edge that links them.
  2. The import: ReferentialIntegrityError is not re-exported from the top-level engrava package. It lives in engrava.domain.exceptions.

Fix. Import it from its real module, and ensure both endpoints exist first:

from engrava.domain.exceptions import ReferentialIntegrityError

try:
    await store.create_edge(edge)
except ReferentialIntegrityError:
    ...  # one endpoint is missing — create the thought, then retry

The exceptions that are re-exported at the top level are EngravaError (the base), ConfigError, EmbeddingModelMismatchError, ExtensionMigrationError, InvalidTransitionError, MindQLParseError, ReadOnlyViolationError, StaleDataError, and ThoughtNotFoundError. Anything else lives under engrava.domain.exceptions.

Still stuck?