Skip to main content

Every click on the reports page navigates to a transaction drill-down in Safari

In Safari, clicking anywhere on the reports page — including the period picker or other controls — could unexpectedly navigate to a category’s transactions drill-down instead of performing the intended action.
The category rows in the reports breakdown use a stretched link whose overlay was anchored to the parent <tr> element. CSS 2.1 §9.3.1 leaves position: relative on table rows undefined, and Safari does not implement it. The overlay therefore resolved against a much larger positioned ancestor, blanketing the entire reports page and intercepting every click. Chrome and Firefox establish the containing block on the row itself, so the issue was invisible in those browsers.
The stretched-link overlay is now anchored to the flex wrapper inside the category cell — a <div>, which is an unambiguous containing block in every browser engine. The clickable area narrows from the whole page to just the category cell (icon, name, and entry count), which is the intended behavior.Upgrade to the latest release to get this fix. No configuration change is required.

Debugging rule failures

When an async rule run fails (for example, during a family sync or after a provider webhook), the error is written to the debug log. To view it:
  1. Go to Settings → Debug
  2. Filter by the rules category
Each entry includes the error message and the rule that failed. This is useful for diagnosing why auto-categorization stopped working after a provider or model change.

When Rules Are Triggered

Rules run automatically during family syncs or can be triggered manually. Understanding when rules execute helps troubleshoot categorization issues.
Rules can be manually triggered in the following ways:
  • Clicking “Re-apply” on an individual rule in /settings/rules
  • Clicking “Apply All” in /settings/rules
  • Running the rake task: bin/rails rules:apply_all[family_id]
Rules with active: true run automatically when a family sync occurs (Family::Syncer.perform_sync):Scheduled syncs:
  • SyncAllJob - runs daily at 2:22 AM for all families
  • SyncHourlyJob - runs every hour (for items that opt-in to hourly syncing)
Triggered syncs:
  • Provider webhooks (Plaid, etc.) - triggers sync which eventually propagates to family
  • Manual sync button on accounts page
  • After CSV imports complete (Import model calls family.sync_later)
Rules are not triggered in these scenarios:
  • Manual transaction creation - Only triggers an Account sync (not Family sync)
  • Manual transaction editing - Same as above, only Account sync runs
  • Inactive rules - Rules with active: false never run automatically, only via manual “Re-apply”
Failures during async rule runs (including auto-categorize provider failures) are written to the debug log. To view them:
  1. Go to SettingsDebug
  2. Look for entries with category rules
Each failed run includes the error message and the rule that triggered it.

Rule run failures not visible in the UI

When rules run asynchronously (during a sync or via a background job), failures are not surfaced in the main UI. If auto-categorization or merchant detection appears to stop working silently, check the debug log.
Failures from background rule runs — including auto-categorize provider errors — are written to the debug log. To view them:
  1. Go to Settings → Debug
  2. Filter by the relevant category (e.g., rules or auto_categorize)
A request that was enqueued but has no matching start entry in the log means the job never reached a worker.

Category Rule Popup Behavior

When a user selects a category for a transaction, a popup may appear to create an automatic categorization rule. This popup is intentionally gated by several conditions:
The popup only shows when all of these conditions are met:
  1. User has not disabled rule prompts (rule_prompts_disabled is false)
  2. User hasn’t dismissed the popup in the last 24 hours
  3. The transaction category actually changed
  4. No existing rule already sets this category for similar transactions
  5. The transaction has a category assigned
Check these common reasons:
  • Recently dismissed: Wait 24 hours after dismissing the popup
  • Rule already exists: A matching rule may already be in place
  • Prompts disabled: Check if rule_prompts_disabled is enabled for the user

Sure Doesn’t Work Over HTTPS

If Sure behaves incorrectly or fails when accessed over HTTPS, it’s usually due to missing SSL-related configuration between Nginx and Rails.
Sure relies on correct protocol headers to determine whether a request is secure.
When HTTPS is terminated at Nginx but not properly forwarded to the app, Rails may treat requests as HTTP.
Apply the following configuration changes:
  1. Nginx Ensure HTTPS is forwarded to the upstream app:
  2. docker-compose.yml In the x-rails-env: &rails_env section, set:
    This tells Rails to treat all requests as HTTPS.

Some pages break over HTTPS

Sure uses WebSockets for certain pages. If some pages fail to render correctly or break entirely, this is often caused by missing SSL or upgrade headers in your reverse proxy configuration.
You may see pages partially load, fail completely, or errors similar to:
This indicates that the WebSocket upgrade request is not being forwarded correctly.
Ensure your Nginx configuration includes the required WebSocket headers:
These headers allow Nginx to properly handle WebSocket upgrade requests over HTTPS.
Self-hosted setups using a forward-auth proxy (Authelia, Authentik, Traefik forward-auth, etc.) may see 500 errors when clicking the Income or Expense drill-down links on the dashboard’s Money In / Out widget. The links work fine when accessed directly.
The drill-down links previously included every accessible account ID as explicit query parameters. With many accounts, the resulting URL could exceed several thousand characters. Forward-auth proxies forward the full URL to the auth service in a request header; if that header exceeds the proxy’s read-buffer or header-size limit, the request fails with a 500 before it reaches Sure.
Sure now omits the account_ids parameter when the widget’s selected accounts exactly match the user’s full set of accessible accounts — the same default the transactions page uses when no filter is applied. The result is identical, but the URL stays short.When a family has accounts excluded from reports or tax-advantaged accounts (so the eligible set differs from the accessible set), the IDs remain explicit to preserve the correct scope.Upgrade to the latest release to get this fix. No configuration change is required.
If you cannot upgrade immediately, increase the header buffer size in your proxy. For Nginx acting as a forward-auth proxy:
For Traefik, set forwardAuth.tls.insecureSkipVerify is unrelated — instead configure your auth service to accept larger headers.

Deleting an account with many transfers is slow

When you delete an account, Sure removes all transfers linked to its transactions. In older versions, this process issued a separate database query for each transfer’s associated transaction records, causing it to slow significantly when an account had hundreds of transfers.
Sure now loads all transfer transaction associations in a single query before starting the deletion loop. Deleting an account with a large transfer history completes in roughly constant time regardless of the number of transfers involved.Upgrade to the latest release to get this fix. No configuration change is required.

Budget totals do not update after deleting transactions

If a budget still shows spending from a transaction that was already deleted, the most likely cause is a stale aggregate cache. Sure hard-deletes the transaction entry, so the deleted transaction is gone; the stale value can come from cached budget or income totals that were not invalidated by deleting an older entry.
Budget totals are derived from entry aggregation queries. Older versions of Sure used the most recent entry update timestamp as part of the cache key, which meant deleting an older transaction could leave the cache key unchanged.
The entry aggregate cache version now includes both:
  • The current entry count
  • The newest entry updated_at timestamp
Because the count changes when an entry is deleted, budget and income aggregate caches are invalidated even when the deleted transaction was not the most recently updated entry.
Confirm the installed version includes the cache invalidation fix for Family#entries_cache_version, then re-check the affected budget after the next request or cache refresh. The regression test is:

Chat fails immediately on a fresh family with a strict OpenAI-compatible provider

Some strict OpenAI-compatible providers (not OpenAI itself) reject chat requests with a schema validation error when a family has no accounts, categories, merchants, tags, or tickers yet.

Rule runs fail silently

If auto-categorization or merchant detection rules appear to do nothing, the background job may have failed without surfacing an error in the UI.
Async rule run failures are now logged to the debug log. Go to Settings → Debug and look for entries from the rule_run category. Each failed run includes the error class, message, and the rule that triggered it.You can also check worker logs directly:
Common causes include:
  • LLM provider errors — The AI provider returned an error or timed out. Check your OPENAI_ACCESS_TOKEN and provider connectivity.
  • Auto-categorize provider failures — The provider returned an unexpected response format. These are now propagated and logged rather than silently swallowed.
  • Worker not running — Rules run in the background via Sidekiq. Verify the worker container is healthy.

App crashes for a family with an invalid timezone

If a family’s timezone setting contains an unrecognized IANA zone name (for example, after a tzdata rename like Europe/KievEurope/Kyiv), Sure now falls back to the application default timezone instead of crashing.

Chat fails immediately on a new account with a strict OpenAI-compatible provider

Some strict OpenAI-compatible providers (not OpenAI itself) reject chat requests with a schema validation error when a family has no tags, merchants, or categories yet. This happens because the assistant’s tool schemas include enum fields built from family data — an empty family produces enum: [], which is invalid JSON Schema.
Sure builds tool schemas for the assistant using family data (account names, categories, merchants, tags, tickers). A family with none of this data produces enum: [] for those fields, which is invalid JSON Schema. OpenAI tolerates it, but strict providers reject the entire request before the model runs. Family#timezone is a free-text field. If the stored value becomes stale — due to a tzdata rename, a database restore from an older backup, or a direct database edit — the app previously raised an ArgumentError on every request for that family, including the login page.
Sure validates the timezone value against ActiveSupport::TimeZone before using it. If the value is unrecognized, the app falls back to the default timezone and logs a warning to Settings → Debug under the timezone category. The log is debounced to once per day per family so it does not flood the debug log.To fix the root cause, update the family’s timezone to a valid IANA zone name from Settings → Profile. Sure builds tool schemas dynamically from your family’s data (account names, categories, merchants, tags, tickers). When none of that data exists yet, the schema contains enum: [], which is invalid JSON Schema. OpenAI tolerates it, but strict OpenAI-compatible providers reject the entire request before the model runs.
Empty enum arrays are now pruned from tool schemas before the request is sent, falling back to a plain string type. This fix applies to both chat tool definitions and the /mcp endpoint’s tools/list.If you are on an older version and cannot upgrade, add at least one account, category, or tag to the family — this populates the enum and avoids the empty-array case.

Chat shows “assistant not available” before the model finishes

Self-hosted users running a local model (Ollama, LM Studio, etc.) may see the chat fail with “assistant not available” even though the model is still generating a reply and tokens are being billed.
Sure uses a whole-turn watchdog (AI_RESPONSE_TIMEOUT, default 90 seconds) that starts when the message is queued. Custom OpenAI-compatible providers use a synchronous code path — nothing renders until the full reply is generated — so the watchdog fires before the model finishes.Tool-using turns make this worse: each tool call adds another full model round. The total time the watchdog must cover is:
With the defaults (ASSISTANT_MAX_TOOL_CALL_ITERATIONS=5, OPENAI_REQUEST_TIMEOUT=60), a worst-case turn can take up to 360 seconds of model time — far beyond the 90-second default.
The most effective approach is to lower ASSISTANT_MAX_TOOL_CALL_ITERATIONS (reducing the worst-case bound) and then size AI_RESPONSE_TIMEOUT using the formula above.For a local Ollama setup:
You can also set Chat Response Timeout from Settings → Self-Hosting → AI Provider in the UI. The environment variable takes precedence.See Chat response timeout for full sizing guidance.

”Add transaction” button does nothing when no account is selected

If you click Add transaction on the new transaction form without selecting an account, the button previously appeared to do nothing (the request returned a 404 error). The form now re-renders with a validation error so you can see what is missing and correct it.

Split transactions not grouped in account activity

When the Group split transactions preference was enabled, split child entries were collapsed under their parent on the Transactions page but still appeared as flat, ungrouped rows on the Activity tab of individual account pages. Split transaction grouping now applies consistently to both the Transactions page and the account activity feed. When the preference is on, split children appear indented under their parent row in both views.

Excluded transactions disappear from account activity

Transactions marked as excluded were previously hidden from the account activity feed entirely. This made it impossible to re-include them because there was no row to click. Excluded transactions now appear greyed-out in the account activity list so you can still open them and toggle exclusion off if needed.

Import fails on rules with no name or deleted rejected transfers

Importing an all.ndjson export could fail with two unrelated errors:
  • Rule with "name": null — The import preflight incorrectly required a name for every rule, but the rule model allows a null name. The preflight now requires the rule’s id instead, matching what the importer actually uses.
  • RejectedTransfer referencing a deleted transaction — A rejected transfer whose original transaction was later deleted caused the import to abort with a hard error. These orphaned rows are now skipped with a warning instead of blocking the entire import.
If a previous import attempt failed for either of these reasons, retry the import after upgrading.

Why is Sure not running auto-categorization and merchant detection on the same transactions again?

Sure caches AI-generated results to avoid redundant API calls and costs. Once a transaction has been processed by AI rules, it won’t be re-processed unless you explicitly reset the AI cache.
When AI rules process transactions, Sure stores:
  • Enrichment records - Which attributes were set by AI (category, merchant, etc.)
  • Attribute locks - Prevents rules from re-processing already-handled transactions
This caching ensures:
  • Transactions aren’t sent to the LLM repeatedly
  • API costs are minimized
  • Processing is faster on subsequent rule runs
To have AI rules re-process transactions, you need to reset the AI cache:
  1. Go to SettingsRules
  2. Click the menu button (three dots)
  3. Select Reset AI cache
  4. Confirm the action
After resetting, the next time rules run (either manually or during a sync), AI will re-process all transactions.Note: This will incur API costs if using a cloud provider.The reset runs in the background. You can track its progress and see how many AI cache entries were removed in Settings → Debug, filtered by the ai_cache_reset category. Each run logs when it starts, how many entries were removed per scope, and any records that could not be cleared.
Common scenarios for resetting the cache:
  • Switching LLM models - Different models may produce better categorizations
  • After system updates - New versions may have improved prompts
  • Fixing miscategorizations - When AI made systematic errors
  • Testing - During development or evaluation of AI features
The AI cache is automatically cleared when you change the OpenAI model setting.
The reset runs as a background job. You can confirm it completed — and see a breakdown of how many AI cache entries were removed — in the debug log:
  1. Go to SettingsDebug
  2. Filter by category ai_cache_reset
You will see:
  • An info entry when the reset is enqueued from the rules page
  • An info entry when the job starts on a worker
  • A completion entry with the number of enrichments deleted, broken down by scope
  • warn entries for any individual records that could not be cleared
  • An error entry if the job could not be enqueued at all
If you see the enqueue entry but no start entry, the job never reached a worker — check that your worker container is running.