Confirmation dialogs could run script from bank-provided names
In earlier versions, confirmation dialogs (such as those shown when deleting a provider merchant, resetting holdings, or replacing a SimpleFIN account) interpolated user-supplied names directly into the dialog body viainnerHTML. Names sourced from bank feeds, brokerage providers, or other family members could contain HTML that the browser executed as script.
Because provider merchant rows are shared across families, a crafted merchant name could reach any user who opened a confirmation involving that merchant.
What was affected?
What was affected?
- Provider merchant deletion confirmations (the merchant name came from bank feeds and was shared across families)
- SimpleFIN account replacement prompts (account name from the bank)
- SnapTrade brokerage and holdings reset confirmations (brokerage name and ticker symbols from the provider)
- Any other confirmation built through
CustomConfirmor the rawturbo_confirmhash that included a record name
How was it fixed?
How was it fixed?
textContent instead of innerHTML. Record names are rendered as plain text regardless of how the confirmation was constructed, so HTML in a name is never parsed or executed.Upgrade to the latest release to get this fix. No configuration change is required.Category avatar preview does not update when picking a color
When editing or creating a category, changing the color using the color picker previously did not update the avatar preview — the icon and background appeared to stay at the original color until the form was saved.What causes this issue?
What causes this issue?
--avatar-color CSS variable to compute both the background (a light tint) and the icon color (the full color). Earlier versions of the color picker updated the avatar’s inline style.color and style.backgroundColor directly instead of setting the variable. Overriding the resolved values bypassed the theme-aware color-mix logic, so the preview stayed stuck at the last saved color.How was it fixed?
How was it fixed?
--avatar-color CSS variable on the avatar element when you select a new color. The avatar’s CSS reads from the variable, so both the background tint and the icon color update immediately as you pick.Upgrade to the latest release to get this fix. No configuration change is required.Account shows an error when a foreign-currency transaction has no exchange rate
If an account holds transactions in a currency that has no exchange rate available for a given date, earlier versions would abort the entire balance calculation for that account, marking it as errored even though all transactions had imported correctly.What causes this issue?
What causes this issue?
How was it fixed?
How was it fixed?
sync category, and the rest of the account’s transactions are still used to calculate balances normally.Holdings with a missing rate are handled differently: they are still counted toward the total at a 1:1 rate (to avoid phantom cash appearing in the cash balance), and the occurrence is also logged.To see which rate was absent, go to Settings → Debug and filter by the sync category. Look for entries from Balance::SyncCache with level warn.Upgrade to the latest release to get this fix. No configuration change is required.How do I resolve the missing exchange rate?
How do I resolve the missing exchange rate?
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.What causes this issue?
What causes this issue?
<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.How was it fixed?
How was it fixed?
<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:- Go to Settings → Debug
- Filter by the
rulescategory
When Rules Are Triggered
Rules run automatically during family syncs or can be triggered manually. Understanding when rules execute helps troubleshoot categorization issues.Manual trigger (user-initiated)
Manual trigger (user-initiated)
- 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]
During family sync (only active rules)
During family sync (only active rules)
active: true run automatically when a family sync occurs (Family::Syncer.perform_sync):Scheduled syncs:SyncAllJob- runs daily at 2:22 AM for all familiesSyncHourlyJob- runs every hour (for items that opt-in to hourly syncing)
- Provider webhooks (Plaid, etc.) - triggers sync which eventually propagates to family
- Manual sync button on accounts page
- After CSV imports complete (
Importmodel callsfamily.sync_later)
When rules do NOT run
When rules do NOT run
- 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: falsenever run automatically, only via manual “Re-apply”
How do I see why a rule run failed?
How do I see why a rule run failed?
- Go to Settings → Debug
- Look for entries with category
rules
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.Where are async rule failures logged?
Where are async rule failures logged?
- Go to Settings → Debug
- Filter by the relevant category (e.g.,
rulesorauto_categorization)
Category Rule Popup Behavior
When you select a category for a transaction, a prompt appears to create an automatic categorization rule. This prompt has two controls:- X (close) — removes this individual prompt only. It makes no server call and does not affect future prompts.
- Hide for today — hides all rule suggestion prompts for 24 hours. A note below the buttons reads “Hide for today pauses these suggestions for 24 hours.” so the behavior is always visible.
When does the prompt appear?
When does the prompt appear?
- You have not disabled rule prompts (
rule_prompts_disabledis false) - You have not clicked Hide for today in the last 24 hours
- The transaction category actually changed
- No existing rule already sets this category for similar transactions
- The transaction has a category assigned
Why isn't the prompt showing?
Why isn't the prompt showing?
- Recently snoozed: You clicked Hide for today — wait 24 hours for prompts to resume
- Rule already exists: A matching rule may already be in place
- Prompts disabled: Check if
rule_prompts_disabledis enabled for your account (set via Don’t show this again)
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.What causes this issue?
What causes this issue?
When HTTPS is terminated at Nginx but not properly forwarded to the app, Rails may treat requests as HTTP.
How do I fix it?
How do I fix it?
-
Nginx
Ensure HTTPS is forwarded to the upstream app:
-
docker-compose.yml
In the
x-rails-env: &rails_envsection, 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.What are the symptoms?
What are the symptoms?
How do I fix it?
How do I fix it?
Money In / Out links return 500 errors behind a forward-auth proxy
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.What causes this issue?
What causes this issue?
How was it fixed?
How was it fixed?
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.Workaround for older versions
Workaround for older versions
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.How was it fixed?
How was it fixed?
Provider connection stuck on “Deletion in progress”
If a provider connection shows “Deletion in progress” indefinitely and never disappears, the background deletion job may not have been queued successfully.What causes this issue?
What causes this issue?
How was it fixed?
How was it fixed?
What should I do if a connection is already stuck?
What should I do if a connection is already stuck?
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.What causes this issue?
What causes this issue?
How was it fixed?
How was it fixed?
- The current entry count
- The newest entry
updated_attimestamp
How can maintainers validate it?
How can maintainers validate it?
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.How do I see rule run failures?
How do I see rule run failures?
rule_run category. Each failed run includes the error class, message, and the rule that triggered it.You can also check worker logs directly:What causes rule runs to fail?
What causes rule runs to fail?
- LLM provider errors — The AI provider returned an error or timed out. Check your
OPENAI_ACCESS_TOKENand 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
workercontainer 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 likeEurope/Kiev → Europe/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 includeenum fields built from family data — an empty family produces enum: [], which is invalid JSON Schema.
What causes this?
What causes this?
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.How is it handled now?
How is it handled now?
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.How was it fixed?
How was it fixed?
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.What causes this?
What causes this?
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: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.How do I fix it?
How do I fix it?
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:”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.Rule category, merchant, or tag names missing in exports
When you export your data (via Settings → Exports), rule conditions and actions that reference a category, merchant, or tag should appear with the human-readable name rather than the raw UUID. In older versions, two bugs could cause the name to be missing or stale in the exportedrules.csv and all.ndjson files:
- Uppercase UUID stored in the rule — Rule operand values are stored as UUIDs. If a UUID was stored in uppercase (for example, after a direct database edit or migration), the export lookup was case-sensitive and would miss the match, leaving the raw UUID in place of the name.
- Stale in-memory association — If a category, merchant, or tag was created or modified during the same request that triggered the export (for example, as part of an import or rule-apply job), the exporter could read a stale in-memory association and produce an outdated name or omit the
value_refentirely.
Rule creation from modal fails with “must have at least one action”
When creating a rule from the rule creation modal, saving the form could fail with a validation error saying the rule must have at least one action — even though you had added one. The re-rendered form also lost any dynamically added action or condition rows.What caused this issue?
What caused this issue?
new_1, new_2 for these rows. Rails strong parameters only treat integer-keyed hashes as nested attributes — it checks that the key matches /\A-?\d+\z/ — so string keys were silently dropped before the record was saved. The result was a rule with no actions, which failed validation.How was it fixed?
How was it fixed?
Date.now() * 1000 + seq). This avoids collisions with the numeric indexes Rails assigns to already-persisted rows on edit forms, and ensures all dynamically added rows pass Rails’ nested-attribute check.Upgrade to the latest release to get this fix. No configuration change is required.Import fails on rules with no name or deleted rejected transfers
Importing anall.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’sidinstead, 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.
Sure import fails with unresolved merchant references from an older export
Exports produced before provider-merchant support was added did not includeProviderMerchant rows in the all.ndjson file. Importing such a file into a current version of Sure would previously fail preflight with a blocking error for every transaction whose merchant_id pointed at a provider-assigned merchant.
What changed?
What changed?
merchant_id on a transaction or recurring transaction is now a preflight warning rather than a blocking error. Sure imports the row without a merchant assigned. The confirm screen shows the number of affected records under a “merchant references not found” notice.Unnamed recurring transaction series that have neither a name nor a resolvable merchant are skipped entirely (and reported separately). Named series without a merchant import normally.How do I get the merchants into the new instance?
How do I get the merchants into the new instance?
ProviderMerchant records referenced by the family’s transactions and recurring transactions. Importing that newer export resolves the merchant references without warnings.If the source instance is no longer available, the missing merchants are rebuilt automatically the next time Sure syncs the family’s connected accounts.Sure import fails because categories or tags already exist on the destination family
When a fresh family is created, Sure seeds it with a default set of categories. Importing a Sure export from another instance into that family previously failed preflight if any exported Category, Tag, or Merchant names matched the existing defaults.What changed?
What changed?
Will my data be correct after the import?
Will my data be correct after the import?
Reading auto-categorization debug logs
When auto-categorization runs, Sure writes structured entries to the debug log under theauto_categorization category. Go to Settings → Debug and filter by auto_categorization to see them.
What entries will I see?
What entries will I see?
- AI categorization cache used (info) — logged when some transactions in the batch already have an AI-set category that matches their current category. Those transactions are skipped to avoid redundant API calls. The entry includes the IDs of the cached transactions.
- AI categorization blocked by enrichment protection (info) — logged when some transactions have a locked
category_idattribute that was not set by AI. Those transactions are protected from being overwritten. The entry includes the IDs of the blocked transactions. - AI categorization failed: no categories available (error) — logged when the family has no categories configured. Auto-categorization cannot run without categories to map against.
- AI categorization completed (info) — logged at the end of a successful run. The entry includes the requested transaction IDs, which transactions were categorized, which were cached or blocked, and the total number of modified transactions.
Why are some transactions not being categorized?
Why are some transactions not being categorized?
auto_categorization entries. Transactions may be skipped because:- They already have an AI-assigned category that matches the current value (cache hit — shown in the cache used entry).
- Their
category_idattribute is locked by a non-AI source (shown in the blocked entry). - The family has no categories set up (shown in the no categories available error entry).
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.Why does Sure cache AI results?
Why does Sure cache AI results?
- Enrichment records - Which attributes were set by AI (category, merchant, etc.)
- Attribute locks - Prevents rules from re-processing already-handled transactions
- Transactions aren’t sent to the LLM repeatedly
- API costs are minimized
- Processing is faster on subsequent rule runs
How do I force Sure to re-process transactions?
How do I force Sure to re-process transactions?
- Go to Settings → Rules
- Click the menu button (three dots)
- Select Reset AI cache
- Confirm the action
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.When should I reset the AI cache?
When should I reset the AI 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
How do I verify the reset actually ran?
How do I verify the reset actually ran?
- Go to Settings → Debug
- Filter by category
ai_cache_reset
- 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