{
 "number": 36326,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/36326",
 "title": "kernel: Use typed errors for fatal and flush error notifications",
 "author": "arejula27",
 "author_association": "CONTRIBUTOR",
 "created_at": "2026-09-24T12:46:30Z",
 "updated_at": "2026-11-01T04:37:18Z",
 "description_hash": "a6ad9e303b0d2c9c",
 "age_days": 38,
 "draft": false,
 "labels": [
  "Validation"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "8a5934f7d28204270c56f445519c6f66259c1df8",
 "head_ref": "kernel-notifications-error-variant",
 "head_repo": "arejula27/bitcoin",
 "head_history": [
  {
   "t": "2026-10-08T22:17:25Z",
   "sha": "8a5934f7d28204270c56f445519c6f66259c1df8"
  }
 ],
 "additions": 454,
 "deletions": 89,
 "changed_files": 15,
 "commit_count": 6,
 "size_bucket": "L",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_ack": [
     {
      "login": "sedited",
      "url": "https://github.com/bitcoin/bitcoin/pull/36326#issuecomment-5814588408"
     },
     {
      "login": "yuvicc",
      "url": "https://github.com/bitcoin/bitcoin/pull/36326#pullrequestreview-5465955822"
     }
    ]
   },
   "conflicts": []
  }
 },
 "acks_parsed": {
  "sedited": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-09-24T12:59:18Z",
   "stale": false
  },
  "yuvicc": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-10-09T07:50:26Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 2,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "Yudis-bit",
   "ryanofsky",
   "sedited",
   "yuvicc"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-10-08T22:17:25Z",
  "last_reviewer_activity": "2026-10-11T09:41:29Z",
  "last_reviewer": "Yudis-bit",
  "author_silent_days": 24,
  "waiting_on_author_days": 21,
  "days_since_update": 0
 },
 "refs": {
  "mentioned": [
   27587,
   35646
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 27587,
    "type": "issue",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "Bitcoin Kernel Library Project Tracking"
   },
   {
    "number": 35646,
    "type": "pull",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "RFC: Separate out runtime errors from BlockValidationState using `util::Expected`"
   }
  ],
  "conflicts": []
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/kernel/bitcoinkernel.cpp",
  "src/kernel/error.h",
  "src/test/CMakeLists.txt",
  "src/validation.cpp"
 ],
 "body": "Part of the libbitcoinkernel project (tracking issue #27587 ), specifically the \"Remove translation.h usage from the kernel\" item. `kernel::Notifications::fatalError()` and `flushError()` take a `bilingual_str`, which pulls `util/translation.h` and the application-level `G_TRANSLATION_FUN` into the kernel library, and translation is an application concern rather than a consensus one. The same interface already shows the way out: `warningSet()` takes a `kernel::Warning` enum, and the node turns it into a translated string in `node/kernel_notifications.cpp`.\n\nAlong with removing the dependency, another goal here is separating responsibilities, because the goals of error handling and error reporting are different: presenting an error to a user is an application concern, not something `libbitcoinkernel` itself should own. The error is structured on both sides: in C++ as the full `kernel::FatalError` variant, and in the C API as its numeric value. With the current changes the C side only carries half of that structure today though, the dynamic fields (a block hash, a path, an exception's `what()`) do not cross the C API yet, even though ideally they should.\n\nThis PR does the same for the two error notifications. A new `src/kernel/error.h` defines `kernel::FlushError` as a plain enum and `kernel::FatalError` as a `std::variant` with one struct per error, whose fields carry the context the message needs: a block hash, a path, the `what()` of a caught exception, block heights. It holds types and nothing else, so every message is now built on the node side. The exception is `FatalErrorString()` in `validation.cpp`, because a fatal error raised during validation also fails the block through `BlockValidationState::Error()`, which takes the reason as a plain string right there, and `fatalError()` returns void, so the node has no way to put its message into that state. A follow-up could store the typed error in the state instead and let the node create the string message.\n\nThis does not remove the kernel's dependency on `util/translation.h`, it is just a step. The other two notifications, `progress()` and `warningSet()`, still take a `bilingual_str`, and `bitcoinkernel.cpp`, `checks.cpp`, `node/chainstate.cpp`, `txmempool.cpp` and `validation.cpp` still include the header.\n\nThere is one behavior change to mention here. The two C API callbacks lose their `message` parameter, so they now carry only the error code, the same pattern that `btck_BlockValidationResult` already uses.",
 "commits": [
  {
   "sha": "af5ed188f16f749a431772e7d4253e0f4f20b9b2",
   "date": "2026-10-08T22:10:03Z",
   "message": "kernel: Add FatalError and FlushError types"
  },
  {
   "sha": "54bc6a5f94c0d7f049ebc156f305019a33e280fd",
   "date": "2026-10-08T22:10:23Z",
   "message": "kernel: Use FlushError enum in Notifications::flushError\n\nTranslation is an application concern, so the node builds the message.\nThe C API callback takes the error code alone."
  },
  {
   "sha": "cdd475770652dbbc2e9c292281c0670ea42a686f",
   "date": "2026-10-08T22:11:07Z",
   "message": "kernel: Use FatalError variant in Notifications::fatalError\n\nSame as the previous commit, with each error's dynamic context carried as\ntyped fields. std::visit keeps the node's message table exhaustive.\n\nnode/blockstorage.cpp no longer needs util/translation.h."
  },
  {
   "sha": "6daa91c2a673fb90950ec47c6cb2fc672cf953e3",
   "date": "2026-10-08T22:11:07Z",
   "message": "node: Add KernelNotifications::abort()\n\nMoves the m_shutdown_on_fatal_error check into a single place, so that node\ncode aborting without a kernel error can reuse it."
  },
  {
   "sha": "55611f2900a260af30ae83382e354d4c3201ca9c",
   "date": "2026-10-08T22:11:07Z",
   "message": "kernel: Drop FailedToStartIndexes from the fatal error set\n\ninit.cpp is not part of libbitcoinkernel, so no consumer of the library\ncould ever be notified of this error; it now aborts through AbortNode()\ndirectly."
  },
  {
   "sha": "8a5934f7d28204270c56f445519c6f66259c1df8",
   "date": "2026-10-08T22:11:07Z",
   "message": "kernel: Type the snapshot coins db rename failure\n\nIt was forwarded as a plain string, so the node could only re-emit it\nuntranslated; InvalidateCoinsDBOnDisk() now returns the two paths and the\nnode builds the message, which also takes the last _() out of the kernel's\nfatal error paths."
  }
 ],
 "timeline": [
  {
   "t": "2026-09-24T12:59:18Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "Concept ACK :)",
   "id": "c:5814588408",
   "url": "https://github.com/bitcoin/bitcoin/pull/36326#issuecomment-5814588408",
   "hash": "affb319c8627e520",
   "edited": null
  },
  {
   "t": "2026-09-24T14:50:44Z",
   "kind": "comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nInteresting. I didn't know this was a goal. Has this been discussed somewhere? Do we want to have no ability for the kernel to show detailed translated messages when something fails in the kernel, like \"Bad undo data in block {hash} height {height} file {filename}\" (to pick a random example)? Is this because we want (1) all kernel messages to be untranslated? Or (2) all messages that are translated to be represented as structs and enums in the kernel and made into strings by applications? Or some combination of these approaches?\n\nI do think these approaches are reasonable, but they do come with costs. Approach (1) makes harder to write polished applications that are localized and do not require english. Approach (2) is costly to developers and users because it either results in vague messages that do not include important details necessary to understand, or it requires complicated structural representations of exceptional conditions and statuses in code.\n\nIMO, util/translation.h is a low overhead way to provide helpful error and status messages that can be easily translated as needed. I don't see significant benefits to removing it and don't think it should be a goal to remove.\n\nWhich is not to say that providing structural error information is not useful at all. Right now we do not provide enough structural error information and should provide more. But it is also possible to take things too far and add too much structure. We should not be creating a situation where calling any kernel API means needing to handle a half-dozen error codes, when a simple error status and a translated string would be easier to handle in code and provide more detailed and helpful information to users.",
   "id": "c:5816461348",
   "url": "https://github.com/bitcoin/bitcoin/pull/36326#issuecomment-5816461348",
   "hash": "b2883fe0cd6adbde",
   "edited": null
  },
  {
   "t": "2026-09-25T15:31:51Z",
   "kind": "comment",
   "who": "arejula27",
   "assoc": "CONTRIBUTOR",
   "text": "@ryanofsky thanks for sharing your opinion!\n\nI don't know if this has been discussed in more depth than the checklist item in #27587 from i took inspiration. @sedited might be able to answer that better. If more discussion is needed, we can keep it here on the PR, or I (or someone else, not sure about the process) can start a thread on the mailing list, whatever works best.\n\nGiven the two directions you describe, I was thinking on the 2 when writing the PR.\n\nI think this is less about the dependency itself (despite removing it is a nice win for me), `util/translation.h` is small, as you say, and more about scope: should the kernel be responsible for producing user-facing output messages at all? The way I see the `libbitcoinkernel` project (which can be wrong or misunderstood), it is not a module of Core, but a library that uses Core's consensus code and can be used anywhere else, to build any application that needs consensus, not only a node that writes to debug.log. Bark, for example, uses the `rust-bitcoinkernel` bindings in their test suite (see this [tweet](https://x.com/matthewvuk2/status/2079991850115756488)), and that kind of consumer is exactly the case where a fixed string is not the right fit. Handing back a string (and I mean any fixed string, not just a `bilingual_str`) is already an application-level decision (how to log, how to present in a GUI), and the kernel should not be the one making it, it forces the clients to parser the message in case they want to handle it. It also limits the library's own clients: if the kernel ships an already-written message, it pushes the consumer toward the kernel's wording instead of whatever the application wants to say. A structured type does not have that problem, the consumer decides all of it.\n\nOn the complexity you are worried about: this set is small and closed. There are 18 cases in total right now, 16 `FatalError` variants plus 2 `FlushError` ones, and I do not expect it to grow, these are internal validation/IO failure modes, not an open API surface. It already shrank by one in this branch (`FailedToStartIndexes`, dropped because it was unreachable from any real kernel consumer).\n\nOn your example, \"Bad undo data in block {hash} height {height} file {filename}\", option (2) does not have to produce a vaguer message. `AssumeutxoDataNotFound`/`SnapshotValidationFailed` and similar carry the typed fields needed (a block hash, heights, a path), and the node still builds that exact detailed, translated message (see `FatalErrorMessage` in `kernel_notifications.cpp`), nothing is lost. It is true that right now the C API loses that message: it only hands back the numeric code. I was thinking about sending the dynamic part of the error the same way I do for the enum itself, maybe through a union (instead of a variant), but that might push manual maintenance work onto the bindings (that is the reason I did not do it in the first approach, worth a discussion).\n\nOn \"calling any kernel API means needing to handle a half-dozen error codes\": I see that as a positive. Handling the error would be the application's responsibility, and I think that is what lets the kernel stay smaller and closer to consensus, further away from logging or presenting errors. The C API just hands back the numeric code (`btck_FatalError`); no message is exposed, only the code, the same precedent `btck_BlockValidationResult` already set. Building anything out of it is entirely up to the consumer. The node (in this repo) is the application that wants the full detailed message, so it is the one place doing an exhaustive `std::visit` over all the variants (other consumers are free not to be that exhaustive).\n\nAll of the above is just my own opinion. As a fairly recent contributor, I am not trying to impose any of this, just making proposals I am happy to change and discuss.",
   "id": "c:5835018112",
   "url": "https://github.com/bitcoin/bitcoin/pull/36326#issuecomment-5835018112",
   "hash": "c8e62f38b17e5c51",
   "edited": "2026-09-25T15:32:32Z"
  },
  {
   "t": "2026-09-25T23:15:00Z",
   "kind": "comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "text": "Thanks, and I want apologize because my last comment was just based on an initial reaction and not very constructive.\n\nI do agree with the direction of the PR and approach of adding structured error types in a [src/kernel/error.h](https://github.com/arejula27/bitcoin/blob/1d66b88dcd0f4608ea151d306547f41a56f29d26/src/kernel/error.h) file. Right now kernel functions don't return enough structured error information and they should return more.\n\nI just want to caution against trying to use types intended for error handling for error reporting as well, because the goals of error handling and error reporting are different. For error handling you generally want fewer branching cases to avoid code complexity and bugs, while for error reporting you usually want more detail. I was also just surprised to see the goal of removing the translation header because it is so small and it was unclear what benefits that removal would bring.\n\nOverall direction of the PR seems good, but would maybe just want to clarify the goals a little more. I think there are a number of problems worth addressing in the error handling area, the main ones being that errors are returned by callback instead of return value, which makes them difficult to handle, and that we don't have good error type, and to extend we do there is no type safety so a function that can actually only return 3 errors looks like it returns 15 errors and caller have to deal with unnecessary ambiguity and mess.\n\nBut looking at actual changes in this PR they seem reasonable, and like something that could be built on to fix other problems. The one thing I would change is that it seems like btck_NotifyFatalError and btck_NotifyFlushError are losing all details about errors that occur because messages are being replaced by codes. This seems like it could cause real harm to kernel applications because there could be no clean way to extract and present error information to users. But could be fixed by keeping the error messages and adding the codes instead of replacing the messages with codes",
   "id": "c:5840933870",
   "url": "https://github.com/bitcoin/bitcoin/pull/36326#issuecomment-5840933870",
   "hash": "77c5a6263fbaae16",
   "edited": "2026-09-25T23:18:10Z"
  },
  {
   "t": "2026-09-26T12:18:26Z",
   "kind": "comment",
   "who": "arejula27",
   "assoc": "CONTRIBUTOR",
   "text": "Appreciate that, @ryanofsky. I have updated the PR description, hope it is what you wanted.\n\nI would also like to recommend reviewers take a look at #35646 (I just left a review there): although DrahtBot does not flag it as a conflict, both PRs define a `kernel::FatalError` type with the same name but different goal and the main purpose of the PR is other.",
   "id": "c:5846207782",
   "url": "https://github.com/bitcoin/bitcoin/pull/36326#issuecomment-5846207782",
   "hash": "8ffbc461304761ad",
   "edited": "2026-09-26T14:15:19Z"
  },
  {
   "t": "2026-10-08T22:17:25Z",
   "kind": "force_push",
   "who": "arejula27",
   "commit": "8a5934f7d28204270c56f445519c6f66259c1df8",
   "id": "push:8a5934f7d28204270c56f445519c6f66259c1df8"
  },
  {
   "t": "2026-10-09T05:11:33Z",
   "kind": "review_comment",
   "who": "yuvicc",
   "assoc": "CONTRIBUTOR",
   "path": "src/kernel/error.h",
   "commit": "af5ed188f16f749a431772e7d4253e0f4f20b9b2",
   "in_reply_to": null,
   "text": "af5ed188f16f749a431772e7d4253e0f4f20b9b2 commit message can be more descriptive.",
   "id": "rc:4226826161",
   "url": "https://github.com/bitcoin/bitcoin/pull/36326#discussion_r4226826161",
   "hash": "22297b01f3b58961",
   "edited": "2026-10-09T07:50:26Z"
  },
  {
   "t": "2026-10-09T05:26:23Z",
   "kind": "review_comment",
   "who": "yuvicc",
   "assoc": "CONTRIBUTOR",
   "path": "src/kernel/bitcoinkernel.cpp",
   "commit": "54bc6a5f94c0d7f049ebc156f305019a33e280fd",
   "in_reply_to": null,
   "text": "can be marked `constexpr` & named `to_btck` to match naming convention?",
   "id": "rc:4226909227",
   "url": "https://github.com/bitcoin/bitcoin/pull/36326#discussion_r4226909227",
   "hash": "18fc83d309fa117d",
   "edited": "2026-10-09T07:50:26Z"
  },
  {
   "t": "2026-10-09T05:26:47Z",
   "kind": "review_comment",
   "who": "yuvicc",
   "assoc": "CONTRIBUTOR",
   "path": "src/kernel/bitcoinkernel.cpp",
   "commit": "cdd475770652dbbc2e9c292281c0670ea42a686f",
   "in_reply_to": null,
   "text": "Same here! See [comment](https://github.com/bitcoin/bitcoin/pull/36326/changes/cdd475770652dbbc2e9c292281c0670ea42a686f#r4226909227)",
   "id": "rc:4226911167",
   "url": "https://github.com/bitcoin/bitcoin/pull/36326#discussion_r4226911167",
   "hash": "744c653649e9e5d7",
   "edited": "2026-10-09T07:50:26Z"
  },
  {
   "t": "2026-10-09T05:30:23Z",
   "kind": "review_comment",
   "who": "yuvicc",
   "assoc": "CONTRIBUTOR",
   "path": "src/kernel/bitcoinkernel.cpp",
   "commit": "cdd475770652dbbc2e9c292281c0670ea42a686f",
   "in_reply_to": null,
   "text": "I think with this change this method reduces each variant to an error code losing the error details (block hash, paths, snapshot recovery info). For e.g.  `SystemErrorWhileSavingBlock` now reports only `SYSTEM_ERROR_WHILE_SAVING_BLOCK` consumers cannot reconstruct the underlying cause from the callback.\n\nMaybe exposing through some error object could be a way, not sure though!",
   "id": "rc:4226931573",
   "url": "https://github.com/bitcoin/bitcoin/pull/36326#discussion_r4226931573",
   "hash": "0bedf677c74aaace",
   "edited": "2026-10-09T07:50:26Z"
  },
  {
   "t": "2026-10-09T06:01:08Z",
   "kind": "review_comment",
   "who": "yuvicc",
   "assoc": "CONTRIBUTOR",
   "path": "src/validation.cpp",
   "commit": "cdd475770652dbbc2e9c292281c0670ea42a686f",
   "in_reply_to": null,
   "text": "This can now return an empty string if the variant isn\u2019t one of the listed types, which wasn\u2019t possible before. In practice, though, every caller passes one of the listed types.",
   "id": "rc:4227101927",
   "url": "https://github.com/bitcoin/bitcoin/pull/36326#discussion_r4227101927",
   "hash": "fef54dcae9ba0149",
   "edited": "2026-10-09T07:50:26Z"
  },
  {
   "t": "2026-10-09T06:12:10Z",
   "kind": "review_comment",
   "who": "yuvicc",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/CMakeLists.txt",
   "commit": "cdd475770652dbbc2e9c292281c0670ea42a686f",
   "in_reply_to": null,
   "text": "should come before `key_io_tests.cpp`?",
   "id": "rc:4227179437",
   "url": "https://github.com/bitcoin/bitcoin/pull/36326#discussion_r4227179437",
   "hash": "b0a8e5af67405090",
   "edited": "2026-10-09T07:50:26Z"
  },
  {
   "t": "2026-10-09T07:50:26Z",
   "kind": "review",
   "who": "yuvicc",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "8a5934f7d28204270c56f445519c6f66259c1df8",
   "text": "Concept ACK\n\nI haven\u2019t looked into this in depth yet. I\u2019ll try to review it more closely alongside https://github.com/bitcoin/bitcoin/pull/35646. Meanwhile, I've left some nits.",
   "id": "r:5465955822",
   "url": "https://github.com/bitcoin/bitcoin/pull/36326#pullrequestreview-5465955822",
   "hash": "6123873a169a8426",
   "edited": null
  },
  {
   "t": "2026-10-11T09:41:29Z",
   "kind": "comment",
   "who": "Yudis-bit",
   "assoc": "NONE",
   "text": "FatalErrorString in src/validation.cpp returns {} for the eight variants that are only routed through Notifications::fatalError(). Having an Assume(false) before the fallthrough return {}; would catch any accidental use of those variants in FatalError(notifications, state, ...) in debug builds, preventing BlockValidationState::Error() from silently absorbing an empty reject reason.\n\nFor btck_NotifyFatalError, passing both the code and the formatted message would mirror btck_NotifyWarningSet, which already supplies both btck_Warning warning and (const char* message, size_t message_len). That preserves programmatic dispatch for C consumers without dropping dynamic details like paths or block hashes.",
   "id": "c:6107685559",
   "url": "https://github.com/bitcoin/bitcoin/pull/36326#issuecomment-6107685559",
   "hash": "09dcf9861f8bd580",
   "edited": null
  }
 ],
 "labels_log": [
  {
   "t": "2026-09-24T12:46:33Z",
   "action": "labeled",
   "label": "Validation",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-24T13:49:54Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-10-08T16:12:52Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-10-08T23:02:57Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-10-08T23:45:42Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-10-07T12:35:28Z",
   "kind": "convert_to_draft",
   "who": "DrahtBot"
  },
  {
   "t": "2026-10-08T22:17:32Z",
   "kind": "ready_for_review",
   "who": "arejula27"
  }
 ],
 "text_chars": 13515,
 "text_tokens_estimate": 3378,
 "changed_paths": [
  "src/bitcoin-chainstate.cpp",
  "src/init.cpp",
  "src/kernel/bitcoinkernel.cpp",
  "src/kernel/bitcoinkernel.h",
  "src/kernel/bitcoinkernel_wrapper.h",
  "src/kernel/error.h",
  "src/kernel/notifications_interface.h",
  "src/node/blockstorage.cpp",
  "src/node/kernel_notifications.cpp",
  "src/node/kernel_notifications.h",
  "src/test/CMakeLists.txt",
  "src/test/kernel/test_kernel.cpp",
  "src/test/kernel_error_tests.cpp",
  "src/validation.cpp",
  "src/validation.h"
 ],
 "files": [
  {
   "path": "src/bitcoin-chainstate.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/init.cpp",
   "add": 1,
   "del": 2
  },
  {
   "path": "src/kernel/bitcoinkernel.cpp",
   "add": 40,
   "del": 4
  },
  {
   "path": "src/kernel/bitcoinkernel.h",
   "add": 37,
   "del": 4
  },
  {
   "path": "src/kernel/bitcoinkernel_wrapper.h",
   "add": 28,
   "del": 4
  },
  {
   "path": "src/kernel/error.h",
   "add": 90,
   "del": 0
  },
  {
   "path": "src/kernel/notifications_interface.h",
   "add": 7,
   "del": 2
  },
  {
   "path": "src/node/blockstorage.cpp",
   "add": 11,
   "del": 10
  },
  {
   "path": "src/node/kernel_notifications.cpp",
   "add": 80,
   "del": 3
  },
  {
   "path": "src/node/kernel_notifications.h",
   "add": 10,
   "del": 2
  },
  {
   "path": "src/test/CMakeLists.txt",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/test/kernel/test_kernel.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/test/kernel_error_tests.cpp",
   "add": 91,
   "del": 0
  },
  {
   "path": "src/validation.cpp",
   "add": 38,
   "del": 47
  },
  {
   "path": "src/validation.h",
   "add": 12,
   "del": 3
  }
 ],
 "test_lines": 100,
 "git": {
  "head": "8a5934f7d28204270c56f445519c6f66259c1df8",
  "head_matches_backup": true,
  "base": "f739c7876b81304bdc72e7eaa307702da269ac31",
  "patch_id": "03197ed82cf53bf4",
  "commits": [
   {
    "sha": "af5ed188f1",
    "subject": "kernel: Add FatalError and FlushError types",
    "files": 1,
    "add": 86,
    "del": 0
   },
   {
    "sha": "54bc6a5f94",
    "subject": "kernel: Use FlushError enum in Notifications::flushError",
    "files": 9,
    "add": 61,
    "del": 16
   },
   {
    "sha": "cdd4757706",
    "subject": "kernel: Use FatalError variant in Notifications::fatalError",
    "files": 14,
    "add": 287,
    "del": 67
   },
   {
    "sha": "6daa91c2a6",
    "subject": "node: Add KernelNotifications::abort()",
    "files": 2,
    "add": 9,
    "del": 1
   },
   {
    "sha": "55611f2900",
    "subject": "kernel: Drop FailedToStartIndexes from the fatal error set",
    "files": 7,
    "add": 9,
    "del": 17
   },
   {
    "sha": "8a5934f7d2",
    "subject": "kernel: Type the snapshot coins db rename failure",
    "files": 5,
    "add": 32,
    "del": 18
   }
  ],
  "patch_truncated": true
 },
 "patch_id": "03197ed82cf53bf4",
 "input_hash": "23c358581cd4123a",
 "extracted_at": "2026-11-02T01:09:49+00:00"
}