feat(code): warn before expensive cold-cache turns - #5439
Merged
Conversation
Mason Daugherty (mdrxy)
marked this pull request as ready for review
August 12, 2026 01:27
Mason Daugherty (mdrxy)
force-pushed
the
mdrxy/code/cold-cache-warning
branch
from
August 14, 2026 20:50
1b9ad13 to
a0fb2c2
Compare
Mason Daugherty (mdrxy)
force-pushed
the
mdrxy/code/cold-cache-warning
branch
3 times, most recently
from
August 17, 2026 06:17
dd4d340 to
07c156a
Compare
Mason Daugherty (mdrxy)
force-pushed
the
mdrxy/code/cold-cache-warning
branch
from
August 17, 2026 21:42
3b6db23 to
6a95f6b
Compare
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
When a runtime model override fails with ModelConfigError, the middleware falls back to the original model but the checkpoint update associated the call with the rejected override spec, letting the TUI resolve cache policy and pricing for the wrong model on the next turn. Persist the resolved spec — the model that actually served the request — instead.
Say the cached prefix "has likely expired" since providers may retain the cache past the documented TTL, drop the output-cost disclaimer and the /clear hint, and rename the Cancel button to "Cancel & keep draft" to signal the message text is restored to the prompt input.
Warn when a resumed thread's checkpointed `_last_model_request_at` fails to parse or no usable `_last_cache_model_spec`/`_model_spec` identity is found, since both conditions silently changed cold-cache warning behavior (warnings disabled until the next successful request, or every send treated as a model change). The in-session re-check in `_cold_cache_warning_for` logs at debug to avoid repeating the resume-time warning.
The warning already keys off the provider-documented retention window; adding an undocumented 5s buffer let turns slip through just past the window without a warning. Compare against window_seconds directly.
At $0.10 the modal fired for a Sonnet conversation of only ~29k context tokens after each 5-minute cache expiry — an ordinary session — so users saw it after every short break and learned to dismiss it reflexively. $0.50 moves the trip point to ~145k tokens on Sonnet, closer to a material surprise charge, while still catching Opus-class re-warms well below that.
- Apply Anthropic's per-model cache minimums (512/2048/4096 tokens)
instead of a flat 1024, which suppressed warnings on Opus 5-class
models and fired false warnings on Haiku 4.5 / Opus 4.5+.
- Drop "3600s" from the Anthropic ttl check; the SDK types ttl as
Literal["5m", "1h"], so the alias could never match a real value.
- Flip OpenAI confidence labels: the GPT-5.6+ 30-minute window is the
documented guaranteed minimum ("expired" past it), while in_memory
and 24h retentions are documented maximums ("may_be_cold").
…ache modal The cold-cache warning modal now offers four choices instead of a bare send/keep-draft prompt: send once, send and mute the warning for the rest of the session (in-memory flag, bypassed by the debug env var so the modal stays reachable while developing), send and persist the suppression to [warnings].suppress in config.toml (re-enableable from /notifications), or keep the draft. Rows are keyboard-only to avoid an accidental click authorizing spend, matching the update-available modal. Also harden suppress_warning/unsuppress_warning against a hand-edited non-table [warnings] section (e.g. warnings = []): they now log and return False instead of raising AttributeError, which in the modal's detached continuation would have silently dropped the submitted message. The persistence call is additionally wrapped in a defensive try/except so a failure surfaces a toast and the send still proceeds.
OpenAI documents the 30-minute GPT-5.6+ lifetime as a guaranteed minimum that may be retained longer, so past the window the prefix is only possibly cold; "expired" would have shown "has likely expired" copy.
The app's priority shift+tab -> toggle_auto_approve binding consumed the
key before ColdCacheWarningScreen's own shift+tab binding could fire,
silently swallowing reverse navigation. Replace the hardcoded screen
tuple in action_toggle_auto_approve with a runtime-checkable
_SupportsReverseNav protocol so any modal implementing action_move_up is
routed automatically, and add an app-level keypress regression test —
bare-App modal tests cannot see the interception. Also clarify the
cancel copy ("Don't send (keep draft)" / "Esc cancel") and document
the trap in libs/code/AGENTS.md.
Price an OpenAI cache miss at the plain input rate. The `generic` write bucket forwarded a `cache_write` detail key, which the catalog bills at a 25% write premium OpenAI does not charge, overstating both the displayed cost and the threshold comparison (gpt-5.6-terra at 100k tokens showed $0.25 against a true $0.20). Stop honoring a user-supplied `cache_control.ttl`. `AnthropicPromptCachingMiddleware` runs inside `ConfigurableModelMiddleware` and overwrites `model_settings["cache_control"]` with its own 5m TTL, so a configured `1h` never reached the wire while the policy granted an hour of retention -- suppressing the warning for 55 minutes of a dead cache. Label OpenAI's `in_memory` and `24h` windows `expired` rather than `may_be_cold`. Both are documented maximums, so once the window passes the entry is gone; only the GPT-5.6+ guaranteed minimum can outlive its window. This also stops the modal from describing a maximum as a "minimum cache-retention window". Fail closed where the confirmation could not be made: - A modal that fails to render now cancels and restores the draft instead of treating the failure as authorization to spend. - Losing the modal slot keeps the draft, so the "answer the other prompt first" toast is no longer contradicted by a silent send. - `_checkpoint_command` writes the request time only alongside a known model spec, making the "timestamp without identity" state -- which read back as a permanent model change -- unreachable at its source. - The resume path passes raw checkpoint values to `_sync_cache_state_from_state`, so malformed values log the same discard warning on every entry point instead of being silently pre-filtered. - `DEBUG_COLD_CACHE` overrides persisted suppression, so it is a true force rather than a no-op for anyone who chose "don't warn again ever". `PromptCachePolicy` is keyword-only: `window_seconds` and `minimum_tokens` are adjacent bare ints, and transposing them positionally built a plausible-looking policy that silently mispriced and mis-gated. Add coverage for the gates that had none: identity change by spec and by params inside the window, below-threshold rejection, `threshold == 0`, slash-command skip, the no-clobber guard on partial state reads, blank checkpointed specs, request-start-not-completion timestamping, and the click-swallow invariant the modal documented but never verified.
Signed-off-by: Mason Daugherty <github@mdrxy.com>
`test_cleanup_refreshes_git_branch` drove four `git` subprocesses through `asyncio.to_thread` to stand up a repo on a `feature` branch. Once the rest of `test_app.py` had run ahead of it, `subprocess.run` intermittently sat in `communicate()` waiting on the captured pipes until pytest-timeout killed the test at 30s -- passing in isolation and failing in full-file runs, with the order dependence making it look like a product bug. The test is about `_cleanup_shell_task` re-resolving the branch, and `_refresh_git_branch` reads the common layout straight from `.git/HEAD`, so the subprocesses were scaffolding rather than coverage. Write `.git/HEAD` directly instead, matching `test_refresh_git_branch_reads_gitdir_pointer` in the same class. Verified with three randomized full-file runs (1499 passed each).
GPT-5.6 and later bill cache writes at 1.25x the uncached input rate, and genai-prices models it. Sending the cold usage payload with no cache-write detail priced a GPT-5.6 miss at plain input, so a turn could skip the cold-cache warning despite exceeding the threshold. Add a generic_write bucket that forwards the write detail for GPT-5.6+ policies; pre-5.6 OpenAI models keep the plain generic bucket, which carries no write detail and prices a miss at the input rate.
- Remove the bold styling from the SEND row so the selection highlight is the only visual emphasis; the bold read as a confusing default marker. - Reword "Send and don't warn again ever" to "Send and never warn again" for clearer contrast with the session-scoped option above it.
The modal's cost figures are worst-case estimates priced from synthetic
usage payloads -- the cache may be partially warm and the actual spend
lower -- so point values like "$0.62" overstate what is known.
- Add `format_cost_estimate` next to `format_cost` (which stays exact
for recorded session spend): two significant figures with a `~`
prefix, halves rounded away from zero via `Decimal` so the bound
never shrinks on a tie, same $0.00 / <$0.01 edge conventions.
- Update the modal body to "may cost up to {cold}" with both figures
rounded; under the `may_be_cold` policy the cost sentence gains an
explicit "If the cache has expired" conditional since the provider
may have retained the cache.
- The threshold comparison keeps using the unrounded estimate; the
rounding is display-only.
Bound the confirmation modal with the standard watchdog. This continuation holds the single `_schedule_off_message_pump` slot, so an await that never resolved left `_modal_command_running()` true forever and stopped the pending queue from draining for the rest of the session, with nothing logged. Guard the post-decision dispatch. The continuation runs outside the caller's `try/except`, so a send failure after the user authorized the spend reached only `_log_task_exception` -- the message vanished with no transcript error. Treat an unusable request time as unknown age rather than a warm cache. A thread checkpointed before cold-cache tracking carries no `_last_model_request_at`, which silently skipped the warning on the first send after resuming a long-idle thread -- the scenario the feature exists for. `identity_changed: bool` becomes `reason: ColdCacheReason` (`idle` / `identity_changed` / `age_unknown`) with its own modal copy, so unknown age is never reported as a model change that did not happen. `age_seconds` is now optional, making "no age" unrepresentable rather than implied. Report an unpriceable warm/cold delta as missing catalog data instead of letting it read as "below threshold", and log every remaining skip: each one is a decision to spend without asking. Normalize the provider before `get_base_url`, which does exact lowercase-key lookups -- `Anthropic:claude-opus-5` reported no custom endpoint and priced a gateway user at official-API rates. Fix the Anthropic minimums table: Haiku 3.5 ships as `claude-3-5-haiku-*`, so the `claude-haiku-3-5` prefix never matched and fell through to the 1,024 default instead of 2,048. Keep shift+tab reverse-nav in the model selector -- `_SupportsReverseNav` matches on method presence, so enrollment is opt-out -- and correct the comment that still named it as a screen reaching the no-op branch. Enforce `RewarmEstimate`'s documented invariants at construction, make it keyword-only, move the debug stand-in policy beside the constants it derives from, fold `_ThreadHistoryPayload`'s presence flag into a single nullable `cache_state`, and add `assert_never` exhaustiveness to both `Literal` branches. Correct docstrings this feature invalidated: `cache_write` is load-bearing for GPT-5.6+ pricing rather than a defensive spelling, `generic_write` is selected by model-name version rather than a catalog check, the debug flag fires on every send rather than the next one, and `format_cost_estimate` keeps cent-level precision below a dime. Align the hook's cost formatting with the modal's so one estimate does not render two ways.
The warning could open and then assert something untrue about the user's configuration, which trains people into the permanent suppression: - An interrupted turn never refreshed the in-memory request time, because only a completed turn reads the checkpoint back. The next send then reported "no record of when this thread last reached the model" seconds after a turn that plainly reached it. A turn that made at least one model request now stamps the identity locally. - `/effort` (and `temperature`, `max_tokens`) compared as a cache change, because the identity check compared whole `model_params` maps. Only `CACHE_IDENTITY_PARAM_KEYS` participate now. A failed model override also wrote `_model_params: None` while the app kept its override, pinning the comparison to a permanent mismatch; that path leaves the checkpointed value alone instead. - GPT-5.6+ ignored an explicit `prompt_cache_retention`, so a user configured for `24h` was warned at 31 minutes and told they had exceeded a 30-minute window they had widened. Retention is a documented maximum and now outranks the 30-minute guaranteed minimum. - `age_unknown` copy stated a retention ceiling for a provider whose window is a floor, inverting the distinction `CacheConfidence` exists to keep. - A request time in the future clamped to zero, reading as maximally warm and suppressing the warning outright; it is treated as unknown age. `ColdCacheWarning` moves to `cold_cache.py` and enforces its documented `age_seconds`/`reason` pairing, and the modal takes the object whole rather than five loose parameters with a defaulted `reason` -- that split let an `age_unknown` warning render "idle for 0m". Failures that send at full price are no longer log-only: evaluation failure notifies once per session, an unparseable `base_url` and a non-finite price warn where they happen, and a failed persistent suppression names its cause instead of blaming file permissions for a malformed `[warnings]` table. Also enforces `RewarmEstimate`'s documented finiteness (`NaN` satisfied neither existing guard), names the `cold-cache` suppression key once, collapses the duplicated threshold loading, and corrects the discard warning that claimed warnings would stay off when they now fire.
`AnthropicPromptCachingMiddleware` overwrites `model_settings["cache_control"]` with its own TTL on every Anthropic request, so a user-supplied value never reaches the wire. Keeping the key in `CACHE_IDENTITY_PARAM_KEYS` made changing an ignored setting read as `identity_changed`, popping the "prefix cannot be reused" modal on a false premise.
Mason Daugherty (mdrxy)
force-pushed
the
mdrxy/code/cold-cache-warning
branch
from
August 18, 2026 01:34
6a95f6b to
a37a4c5
Compare
…-warning # Conflicts: # libs/code/tests/unit_tests/test_app.py
Mason Daugherty (mdrxy)
added a commit
that referenced
this pull request
Aug 18, 2026
) Depends on #5439 [Docs](langchain-ai/docs#5524) Some users don't talk to model providers directly — they go through a gateway or corporate proxy (for example, LangSmith's gateway, or a company-internal relay). The cold-cache warning from #5439 stays silent for all of them, because it only trusts the provider's official API address. This PR adds a setting to say "my endpoint plays by the provider's rules," turning the warnings back on: ```toml [warnings] trusted_cache_endpoints = ["smith.langchain.com"] ``` One entry covers every provider routed through that endpoint — no need to repeat it per provider. --- **Why trust is per-endpoint, not global.** The warning's dollar estimates are only accurate if the thing sitting between you and the provider passes your cache settings through untouched and keeps cached data for as long as the provider documents. A proxy that quietly drops those settings would make the warning's numbers fiction. So instead of one blanket "trust everything" switch, you name the specific endpoints you've verified, and everything else stays silent. **One case stays silent even on a trusted endpoint.** LangSmith's gateway can translate between API formats — for example, you can send an OpenAI-shaped request and have it answered by an Anthropic model. During that translation, your cache settings get rewritten to a generic 5-minute cache (or dropped entirely), so the provider's documented cache behavior no longer applies and any estimate would be a guess. When dcode can see a request will take one of these translated routes (the model name carries a cross-provider prefix like `openai:anthropic/claude-...`), it skips the warning rather than show numbers built on wrong assumptions. Requests that stay in their own format — the normal case — are forwarded by the gateway byte-for-byte (verified against the gateway source), so their warnings remain exactly as accurate as going direct. <details><summary>Test plan</summary> - Unit tests cover trusted-endpoint policy resolution, gateway same-format vs cross-format routes, lookalike-host rejection (`notsmith.langchain.com`, `smith.langchain.com.evil.example`), and malformed config tolerance. - App-level tests confirm a trusted gateway endpoint allows the warning through, and a cross-format gateway route suppresses it even when trusted. - Lint, type check, and the cold-cache/manifest suites pass. </details>
Mason Daugherty (mdrxy)
added a commit
to langchain-ai/docs
that referenced
this pull request
Aug 18, 2026
…#5524) Draft documentation for two `deepagents-code` features: - [langchain-ai/deepagents#5439](langchain-ai/deepagents#5439) — warn before expensive cold-cache turns - [langchain-ai/deepagents#5462](langchain-ai/deepagents#5462) — trust user-declared endpoints for cold-cache policies Generated with AI assistance (Deep Agents Code).
Mason Daugherty (mdrxy)
pushed a commit
that referenced
this pull request
Aug 18, 2026
> [!CAUTION] > Merging this PR will automatically publish to **PyPI** and create a **GitHub release**. For the full release process, see [`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md). --- _Release notes preview: keep this section in sync with the package `CHANGELOG.md`. Publish reads the merged CHANGELOG via `release.yml`, not this PR description — keep them aligned anyway so the PR stays an accurate historical record for reviewers and anyone returning later._ --- ## [0.1.57](deepagents-code==0.1.56...deepagents-code==0.1.57) (2026-08-18) ### Features - Added warnings before expensive cold-cache turns and trust user-declared endpoints for cold-cache policies ([#5439](#5439), [#5462](#5462)). - Made the chat input resizable by dragging its top border ([#5524](#5524)). - Added a `multi_select` question type to `ask_user` ([#5097](#5097)). - Added support for ACP approval modes ([#5394](#5394)). - Added `DeepSeek-V4-Pro-0813` to the model picker ([#5512](#5512)). - Show conversation turns alongside message counts ([#5571](#5571)). - Include `TERM_PROGRAM` in the resume hint ([#5548](#5548)). ### Bug Fixes - Report total context after `/offload` ([#5488](#5488)). - Fixed transcript and thread restoration issues, including hydration lag, scrolling resumed threads to the bottom, and hiding empty previous-thread hints ([#5479](#5479), [#5543](#5543), [#5552](#5552)). - Fixed Auto-mode approval handling by binding “yes” to the paired `ask_user` question and avoiding duplicate Auto denial notices ([#5038](#5038), [#5501](#5501)). - Improved reload behavior by keeping the chat input responsive during `/reload`, reporting MCP server changes, and avoiding plugin reload prompt flashes or startup hints ([#5529](#5529), [#5504](#5504), [#5500](#5500), [#5502](#5502)). - Improved dependency update UI by preserving editable fields and hiding dependency details after updates ([#5521](#5521), [#5519](#5519)). - Fixed chat UI polish issues, including detached spacer mount anchors, the unfocused input cursor, and relative timestamp toggle display ([#5516](#5516), [#5258](#5258), [#5503](#5503)). - Refresh the splash version after updates ([#5520](#5520)). _End release notes preview._ --- > [!NOTE] > A **community contributors** list and a **Special thanks** section (crediting the users who filed the issues this release's PRs closed) are appended to the GitHub release notes automatically at publish time (see [Release Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline), step 3). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: langchain-oss-automated-triage[bot] <248757908+langchain-oss-automated-triage[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Docs
When an interactive chat message reaches the front of the queue, dcode now warns before sending it to the model if its prompt cache may need re-warming, showing the estimated cold-input cost and warm-cache delta.
flowchart TD A["Queued message reaches front of queue"] --> B{"Qualifying message?"} B -- "No (e.g. using a slash command, ACP mode)" --> S["Send the message"] B -- "Yes - sent inside the interactive TUI and not a slash command" --> C["Load checkpointed last request time + model identity"] C -- "Missing or malformed:\nlog a warning and treat as cold (see Fig. 1 for specifics)" --> D C -- "Loaded" --> D{"Known cache policy for current model + endpoint?"} D -- "No (e.g. custom endpoint or other provider)" --> S D -- Yes --> E{"Idle past window, age unknown, or cache-affecting params changed?"} E -- "No: within window" --> S E -- Yes --> E2{"Context ≥ minimum cacheable tokens?"} E2 -- "No: tiny context" --> S E2 -- Yes --> F{"Re-warm price delta ≥ threshold?"} F -- "No: below threshold or unpriceable" --> S F -- Yes --> M["Show blocking warning modal"] M -- "Keep draft (Esc)" --> R["Restore draft to chat input"] M -- "Cannot be shown (render failure, or another prompt owns the modal slot)" --> R M -- "Send anyway" --> S M -- "Send + don't warn again this session" --> SS["Mute until restart (in memory)"] --> S M -- "Send + don't warn again ever" --> SP["Persist to warnings.suppress in config.toml"] --> SFig. 1 — the "treat as cold" fallback
The main flow compares the checkpointed identity (what last warmed the cache) against the current model (what's about to run). Policy and cost always come from the current model, which is known from live config — so a missing checkpointed identity doesn't need its own policy; it just forces the comparison to "changed":
flowchart TD subgraph P["Checkpointed identity (written by middleware after each completed model call)"] P1 -- "Request time bad, absent, or in the future" --> P2["Log, discard time:\nthe age is unknown, not zero"] P1 -- "Model spec bad" --> P3["Log warning, discard spec"] end subgraph Q["Current identity (from live config)"] Q1["Effective model spec\n(always resolvable)"] --> Q2["Resolve policy + price against this model"] end P2 -- "No age to compare against a window" --> OUT2["Continue down the cold-cache path:\nreason = age_unknown"] P3 -- "Discarded spec reads as the model having changed in last vs. current" --> OUT["Continue down the cold-cache path:\nidentity_changed = true"] Q2 --> OUT Q2 --> OUT2The three causes are kept distinct because each gets its own modal copy, and reporting one as another states something untrue about the user's configuration. In particular an unknown age is never reported as a model change, which would claim a change that never happened.
The retention windows come from documented provider policies:
prompt_cache_retention: in_memoryprompt_cache_retention: 24hThe window is either a documented maximum or a documented minimum, and the wording follows from which. Anthropic's TTL and OpenAI's
prompt_cache_retentionceilings are maximums, so once the window passes the entry is gone ("expired"). GPT-5.6+'s 30 minutes is a guaranteed minimum that the provider may exceed, so past it the cache can only be called "may be cold".The two OpenAI knobs are independent, which is why the table is ordered as it is:
prompt_cache_retentionstates a maximum lifetime while the GPT-5.6+ guarantee states a minimum one, so an explicitly configured retention is the later and firmer bound and takes precedence on those models too. Warning a user who asked for24hat the 30-minute mark would contradict their own configuration.Anthropic is always 5m regardless of a configured
cache_control.ttl.AnthropicPromptCachingMiddlewareruns insideConfigurableModelMiddlewareand rewritesmodel_settings["cache_control"]with its own TTL (5m, which this stack never overrides), so a user-supplied1hnever reaches the API. Treating it as an hour would suppress the warning for 55 minutes of a cache that died at five.The warning combines three sources of information:
If the estimated re-warm cost reaches your threshold, a modal appears before sending with four keyboard-driven choices. Cost is estimated by pricing two synthetic usage payloads — one billed as a full cache read, one as a cold request — through the ordinary cost-tracking path, so the pricing catalog stays the single source of truth. Both figures are upper bounds and are rounded upward so the displayed estimate never reads lower than the modelled spend.
How a miss is priced follows the model: Anthropic carries a write premium over plain input, GPT-5.6+ bills a miss as a cache write, and OpenAI before 5.6 charges no write surcharge at all, so those misses price at the plain input rate. Tagging a pre-5.6 miss as a write would apply a premium the provider never charges and overstate both the displayed cost and the threshold comparison.
The model identity check looks at the model name plus the invocation parameters that actually select or invalidate a cache entry —
prompt_cache_retention,prompt_cache_key,prompt_cache_options, andcache_control. A change to any of them invalidates the prefix, so it warns regardless of how recent the last turn was. Unrelated knobs are deliberately excluded:/effortrewritesreasoning_effortwholesale, andtemperatureormax_tokensare just as inert for caching, so comparing whole parameter maps would open the modal asserting that the cached prefix "cannot be reused" when nothing about the prefix moved.Anything that prevents the confirmation from being shown keeps the draft rather than sending: if the modal fails to render, or another prompt already owns the modal slot, the message is not dispatched and the text is restored to the chat input. A warning that cannot be presented must not become an implicit authorization to spend. Where a draft genuinely cannot be put back — the input already holds different text, or the active thread changed while the modal was open — the notification says the message was not sent and points at history recall rather than implying it was merely deferred.
Only interactive chat submissions are checked — prompts sent programmatically (e.g. via ACP from an editor client), slash commands, requests to custom endpoints, and requests without pricing information all skip this warning entirely. The default threshold is $0.50; adjust it with
warnings.cold_cache_min_delta_usd.The request time is kept fresh from two directions. A completed turn reads it back from the checkpoint; a turn that reached the model but ended without a readable checkpoint — an interrupt, say — stamps it locally instead. Without that second path the field would never advance past its initial value, and every send after the first would open the modal reporting no record of a turn that plainly happened.
If the checkpointed request time or model identity cannot be loaded (for example, a checkpoint written by a newer version, or a malformed value), dcode logs a warning naming the affected channel and proceeds down the cold-cache path rather than silently assuming the cache is warm: an unusable request time makes the age unknown, and a missing model identity is treated as a model change. A request time in the future — a corrected clock, or a thread synced from a skewed machine — is treated as unknown age too, since clamping it to zero would place it inside every retention window and suppress the warning outright.
Skipping the warning means sending at full cold-cache price, so the paths that skip it on failure are made visible rather than left to the log alone: if the evaluation itself fails, the session says once that cost checks are unavailable; an unparseable configured
base_urlwarns where it is parsed, since it silently disables warnings for that provider; and a pricing catalog that produces a non-finite figure is reported as a data defect rather than reading as "no price published". A failed persistent suppression names its cause — a malformed[warnings]table needs one line of TOML, not thechmodthat a generic "check file permissions" would send the user to.Made by Open SWE
References