Repository navigation
Unified error struct - #191
Merged
Merged
Conversation
The new `error` module gives one record that can describe a failure from any Engine crate. `Error` holds an `ErrorCode`, a `message` for logs, a `prose` text for the model, an optional `Location`, and a `facts` map from `FactKey` to `serde_json::Value`. - `ErrorCode` has 65 unit variants with explicit `#[repr(u16)]` numbers grouped in family hundreds, and `number` returns the number. - The four types have public fields and derive no serde traits. - `Location::new` returns `None` when the end comes before the start. `Location::line` covers one whole line, and the range is empty on line `u32::MAX`. - No code outside the module uses it yet. - The tests check that each code is in `ALL` once and that each number is in its `family` hundred. `ALL` is a hand-written list.
The universal error gains the codes and the fact key that harness failures need, and a default model-facing text for each code. `prose` returns one sentence per code from the new `error-prose.rs`, and the sentences for failures in the prompt itself speak to a model that fixes the prompt. - `RecorderFailed`, `PluginLoadFailed`, and `NoOutput` open the 8xx family, and the new `Run` fact holds a recorder run id. - The docs of the `Path` fact and of `InvalidValue` now cover file paths and missing values. - `prose` is one `match` with one arm per code, under an `#[expect]` of `clippy::too_many_lines`. - A new test checks that every default text is a whole sentence, and `ALL` now lists 68 codes.
Harness and Plugin crates cannot depend on `runvil-types`, so the universal error now reaches them through the two public crates. The `runvil` facade gains an `error` module, and `runvil-plugin` re-exports the same four types at its root. - The facade module holds `Error`, `ErrorCode`, `FactKey`, and `Location`, one `pub use` line each. - `public-api.txt` now lists the four types, every `ErrorCode` variant with its number, and every `FactKey` variant. - No code uses the re-exports yet.
`store_error` gives the one mapping from a `VfsError` to the universal error, for the places where a store failure leaves the VFS. `VfsError` stays the VFS's own error, so the std-only VFS crate does not change. - The code is named after the variant. A variant added to `VfsError` later becomes `Backend` until it is named. - The facts reuse `store_error_fields`, so they hold the same `path`, `valid_up_to`, `rule`, `anchor`, and `count` values a Lua error table holds. - The message is the failure's display text, the prose is the code's default, and the error has no location. - The `runvil` facade re-exports the function as `runvil::vfs::store_error`. No code calls it yet. - New tests cover all 21 variants and the facts each one keeps.
Every id type and both catalogs now reject an id with an `InvalidValue` error instead of the bespoke `IdError`. The message text stays the same, and the rejected text becomes the `Name` fact. - `invalid_id` in `names.rs` builds the error with the code's default prose and no location. It is crate-private. - `FromStr` for `GlobalName`, `ChainId`, `TaskId`, `PackId`, and `ToolId` now has `type Err = Error`. The `parse` functions, `ModelId::new`, `ModelId::gateway`, `ToolCatalog::new`, and `ModelCatalog::new` return the same `Error`. - `runvil::ids` and `runvil-plugin` no longer re-export `IdError`, and the run-log fidelity test names `runvil::error::Error`. - New tests pin the code, the `Name` fact, the message, and the prose for every id type and for a repeated id in each catalog.
`Role::from_str` now rejects unknown text with an `InvalidValue` error instead of the bespoke `ParseRoleError`. The error keeps the old text, names the input, and lists every role. - The message and the prose are both the old `Display` text, because the list of roles tells the reader what to use instead. - The rejected text is the `Name` fact, which replaces the `input` accessor. - `runvil::model` and `client` no longer re-export `ParseRoleError`. - The role test now pins the code, the `Name` fact, the message, the prose, and the missing location.
`ErrorKind::from_str` now rejects text that is no error-kind tag with an `InvalidValue` error instead of the bespoke `ParseErrorKindError`. The error keeps the old text, names the input, and lists every tag. - The message and the prose are both the old text, because the list of tags tells the reader what to use instead. - The rejected text is the `Name` fact, which replaces the test-only `input` accessor. - `runvil-lua` no longer re-exports `ParseErrorKindError`. The shim's unknown-kind refusal still renders the same text, because it formats the error's `Display`. - The kind test now pins the code, the `Name` fact, and the prose.
`Temperature::new` and `CompletionOptions::with_temperature` now reject a temperature with an `InvalidValue` error instead of the bespoke `TemperatureError`. The message text stays the same. - The error names the `temperature` setting in its `Name` fact, and its `Reason` fact is `not_finite` or `out_of_range` in place of the two variants. - The prose gives the valid range. It does not repeat the message. - `models.use` in the Lua crate still picks its finite-number refusal for a value that is not finite, now through `is_finite` instead of a match on the variant. - `runvil::model` and `model` no longer re-export `TemperatureError`. - The temperature test now pins the code, the `Name` fact, both reasons, the message, and the prose.
`tool_schema_new` now refuses a tool schema with a `ToolSchemaInvalid` error instead of the bespoke `ToolSchemaError`. The message text stays the same. - The error names the tool in its `Name` fact. Its `Reason` fact is `invalid_name` or `non_object_schema` in place of the two variants. - The prose is the code's default, which speaks to whoever fixes the tool. - The engine's `BindSchema` variant still boxes the refusal as its cause, now as the universal `Error`, and its test downcasts to that type and checks the code. - `client` no longer re-exports `ToolSchemaError`, and the engine's `model` module doc no longer names it. - The schema test now pins the code, the `Name` fact, both reasons, the prose, and the tool name in the message.
`SecretString::new` now refuses an empty credential with an `InvalidValue` error instead of the bespoke `SecretError`. The message stays `secret must not be empty`. - The `Reason` fact is `empty_secret`. The error holds no part of the secret. - The prose tells the reader to supply a non-empty secret. - `From<SecretError> for GatewayConfigError` is removed. No production code used it, because the environment loader folds the refusal into `None` with `Result::ok`. - The test that pinned the `SecretError` cause of a config error is removed with the conversion, and the source check on the empty key is removed from the leak test. - The empty-credential test now pins the code, the `Reason` fact, the message, and the prose.
The gateway client's setup errors are now `InvalidValue` errors instead of the bespoke `GatewayConfigError`. The environment and endpoint messages keep their text, and every message still omits any key or URL. - `missing_env` and `invalid_env` name the variable in the `Name` fact. Their `Reason` facts are `missing_env` and `invalid_env`, and an endpoint refusal has `invalid_config`. - The prose of a setup error is its message, because the message names the variable or the rule to fix. - An unparsable URL's message now ends with the `url::ParseError` text instead of keeping it as the source. That text names the rule, never the input. - `GatewayChat::from_env`, `GatewayEndpoint::new`, and its `TryFrom` return the universal `Error`. The crate no longer exports `GatewayConfigError`. - The tests check the reason, the variable, and the prose. The source-chain checks are replaced by a check on the parse text in the message, and the no-leak loop stays.
`HostServices::provide` now refuses a provider with an `InvalidValue` error instead of the bespoke `ServiceError`. The refusal messages keep their text. - The error names the id literal in its `Name` fact. Its `Reason` fact is `invalid_id` or `duplicate_id` in place of the two variants. - The prose is the message, which names the id and the rule. - The map still stays as it was on either refusal, and `runvil-plugin` no longer exports `ServiceError`. - The service tests now check the code, the reason, the literal, and the prose through one helper, in place of the matches on the variants.
`PackRegistry::insert` and every Plugin's `install` now refuse a Pack with an `InvalidValue` error instead of the bespoke `InstallError`. The refusal messages keep their text. - The error names the FQN in its `Name` fact. Its `Reason` fact is `invalid_fqn`, `duplicate_pack`, `reserved_pack_id`, or `reserved_name` in place of the four variants. - The prose is the message, which names the Pack and the rule. - The `install` functions of `plugin-std`, `plugin-mcp`, `plugin-user-input`, and `plugin-web` now return `Result<(), Error>`, and their docs name the `duplicate_pack` refusal. - `runvil-plugin` and the `harness` facade no longer export `InstallError`, and `runvil-plugin` drops its `thiserror` dependency. - The registry tests check the code, the reason, the FQN, and the prose through one helper, and the refusal-message test builds each refusal through `insert`. The two Plugin tests check the `duplicate_pack` refusal the same way.
`Error::with_cause` appends a cause, and each cause under it, to the message after `: `, and leaves the prose as it was. A conversion that kept a typed source now keeps its text in the message, where a log reads it, while the model reads only the prose. - A cause whose text is empty, or already in the message, is skipped. - The facade listing in `public-api.txt` gains the method. - Two new tests pin the appended chain, the unchanged prose, and the skip.
A tool call now fails with the universal `Error` instead of the bespoke `ToolError`, and its four kinds become the codes `ToolInvalidArguments`, `ToolBackend`, `ToolTransport`, and `ToolOther`. `tool_error` builds a failure whose message and prose are both the tool's model-safe text, and `with_cause` puts a cause in the message only. - The `Pack` trait, `PackInfo` construct, every Plugin, the Harness performers, the Engine's effect answer, and the Lua dispatch now carry `Error`. `runvil`, `runvil-plugin`, and `runvil-types` export `tool_error` in place of the two types. - The Lua crate's `Error::tool` and the Engine's failure record take the tool's `prose`, so a cause stays out of the text the model reads and out of the stored record. - `tool_failure_tag` keeps the tags a run log stored before: `invalid_arguments`, `backend`, `transport`, and `other` for any other code. - Tests that pinned the typed source or the kind tags are removed. Tests that hid a source now check the cause text in the message and its absence from the prose. A new test pins every tool code's record tag, and one new types test pins `tool_error`. - The two store-answer tests move unchanged to `effect-tests-store.rs`, which keeps `effect-tests.rs` under the 500-line limit.
A model round and a catalog fetch now fail with the universal `Error` instead of the bespoke `CompletionError`, and the 13 `CompletionErrorKind` kinds become model codes of the same names. `completion_error`, `empty_reply`, and `context_overflow` build the failures, and `phrase` gives each model code its fixed phrase. - `ErrorCode` gains `tag`, which gives each code its name in snake_case. The store and model codes keep the tags a run log already holds, and the chat `FailureRecord` uses `code.tag()`. - The prompt token count and the window move to the `Count` and `Limit` facts, and the finish reason moves to the `FinishReason` fact. - A cause goes through `with_cause`, and the Gateway client's `with_detail` appends the escaped provider body. Both go in the message only, so the prose stays free of provider text. - The Lua `ErrorValue` trait gains `table_message`, and the Engine returns the prose of a model failure through it. The Lua table's `message` stays free of provider text and causes. - The chat `FailureRecord` and the `ChatOutcome::Empty` text take the prose. The display text of the Engine's `Error::Completion` now includes the cause and the provider body. - `runvil` exports the four functions in place of the two types, and `harness-gateway-client` drops its re-exports. - Tests that pinned a typed source or the `detail()` accessor are removed. Tests that hid a source or a body now check its text in the message and its absence from the prose. New tests pin every tag, every phrase, and each builder.
A Gateway web search now fails with the universal `Error` instead of the bespoke `GatewaySearchError`. A transport failure is `ToolTransport` and a backend failure is `ToolBackend`, and `tool_error` builds each one with the existing text as its prose. - `search_error` gives the `SearchError` the prose as its text and the universal `Error` as its source, so the text of the search failure does not change. - A send, read, decode, or parse cause goes in the message through `with_cause` and stays out of the prose. - `harness-gateway-client` no longer exports `GatewaySearchError` and `GatewaySearchErrorKind`, and it drops its `thiserror` dependency. - The response tests check the code, the prose, and the cause in the message through `assert_caused`, and the provider tests check the universal cause through `assert_gateway_cause`. The source downcasts to `reqwest::Error` and `FromUtf8Error` are removed.
A recorder now fails with the universal `Error` under `RecorderFailed` instead of the bespoke `RecorderError`, and the in-memory recorder drops its private `MemoryError`. `recorder_error` builds the failure from an error or a message, so each `map_err` call site keeps its shape. - The message is `the run recorder failed` followed by the cause chain, and the prose is the code's default. `display_chain` renders the same text as before. - The memory recorder's refusals add the `Reason` fact `unknown_run` or `run_ended` and the run id as the `Run` fact. - `HarnessError::Recorder` and `PrepareError::Recorder` hold the error in a `Box`, which keeps both `Err` types under the `result_large_err` limit. - `harness-runner` and the `harness` facade export `recorder_error` in place of `RecorderError`. The workshop's `TursoRecorder` and the tee test build their failures through it. - The source downcast tests are removed. The runner tests check the code, the message, the prose, and the memory recorder's facts through `assert_refused`, and the run-log tests compare the message after the prefix with the `LogError` text.
When a cause is itself a universal `Error`, its message starts with its prose, and the outer message often already ends with that same text. `with_cause` now finds the longest head of the cause's text, cut at a `: `, that the message ends with, and appends only what follows it. - A cause with no such head still goes after `: ` whole, and a cause the message already holds is still skipped. - The search is from the right through `rmatch_indices`, so the longest repeated head wins, and an empty head never matches. - A new test pins a universal cause that adds only its own cause, and an unrelated cause with `: ` in its text that goes in whole.
A Plugin that does not open or load now fails with the universal `Error` under `PluginLoadFailed` instead of the public `PluginFailure` and the private `LoadError`. `load_error` builds each failure with the old message text and the `Reason` fact `open`, `missing_entry`, `refused`, `misuse`, `status`, `undescribed`, or `duplicate`. - The prose is the code's default, because the message holds operating system text and library paths. - `with_path` puts a library's path in the `Path` fact, and a repeat puts the repeated Plugin name in the `Name` fact. A failure of a linked Plugin has no `Path` fact. - `insert_plugin` returns `Result<Arc<Plugin>, Error>`, and `PluginReport::failures` returns `&[Error]`. `harness-plugin-loader` and the `harness` facade no longer export `PluginFailure`, and the loader drops its `thiserror` dependency. - The tests read `message` in place of `reason()`. New assertions check the code, the default prose, the `Reason` fact of each failure kind through the `reason` helper, the absent `Path` of a linked Plugin, and the `Path` and `Name` facts of a library failure.
A run report's output now fails with the universal `Error` instead of the bespoke `OutputError`. `report_output` builds the failure from the declared path and the run's outcome, and `not_completed` builds the failure of a run that did not complete. - A run that did not complete and a prompt with no `output:` file fail with `NoOutput` and the `Reason` fact `not_completed` or `undeclared`. - A completed run that left no file fails with `NotFound`, and a refused read fails with the code `store_error` gives the store's failure. Both put the declared path in the `Path` fact and keep the old message text, and a refused read appends the store's failure to the message. - The prose is the code's default. - `RunReport::output` is `Result<String, runvil::error::Error>`, and the `harness` facade no longer exports `OutputError`. - The facade test that built an `OutputError::Vfs` and read its source is removed. `assert_not_completed` checks the code and reason in the runner tests, and the new `files-tests.rs` pins each failure, its message, its prose, and its facts.
Staging a prompt's declared input now fails with the universal `Error` instead of the bespoke `InputFileError`. `stage_input` keeps each old message text, and `input_kind` picks the Engine's kind for the run the failure ends. - Input text the prompt does not declare and a declared input nobody supplied fail with `RequirementsUnmet` and the `Reason` fact `input_undeclared` or `input_missing`. - A store refusal fails with the code `store_error` gives it and appends the store's failure to the message. The missing input and the store refusal put the declared path in the `Path` fact, and every failure has its code's default prose. - `input_kind` gives `RequirementsUnmet` for that code and `Vfs` for any other, as `kind` did for the three variants. - `PrepareError::Input` holds the error in a `Box`. One `reasoned` helper builds every failure with a `Reason` fact, the output failures included. - The three input tests check the code, the message, the prose, and the facts in place of the variants.
Resolving the Host's selected model now fails with the universal `Error` instead of the bespoke `CurrentModelError`. `model_kind` picks the Engine's kind for the run the failure ends, as `kind` did. - A model list the broker cannot fetch keeps the broker's own failure: its code, its prose, and its facts. Its message gains the old `the model catalog could not be fetched: ` prefix, so the recorded message reads as before. - A selection the list lacks fails with `RequirementsUnmet`, the code's default prose, the selected id as the `Name` fact, and the `Reason` fact `model_absent`. - The runner test that built a `CurrentModelError` to render the expected message now writes that message out. Two new unit tests pin the code, message, prose, facts, and kind of each failure.
A failure in a prompt must name the file a reader can open, not the frontmatter name. `Prompt::parse` now takes a `file` label between the source and the execution id, and `Prompt::file` returns it. - The label is the file the source came from, or the literal `prompt` when the source came from no file. The caller supplies it, because the parser never sees a path. - `RunRequest` gains `file`, and `Services` carries it to the parse in `prepare`. - The workshop's `load_agent` returns the agent's file with its source: the folder file's path, or `agents/chat.md` for the embedded built-in. The contract route passes the name it receives as the label. - No error reads the label yet. Parse and Lua failures will use it as the file of their location. - Every test, example, and bench that parses an inline source passes `prompt`, and the fixture tests pass each fixture's path. Two new tests pin `Prompt::file`, and the discovery tests pin the file `load_agent` returns.
`Prompt::parse` now fails with the universal `Error` instead of the bespoke `ParseError`, and the prompt crate drops its internal `Error`, `ParseErrorKind`, `PackListError`, and `FenceViolation`. Each kind becomes a code: `FrontmatterInvalid`, `StructureInvalid`, `FenceInvalid`, `ListEmptyItem`, `LuaCompile`, or `Internal`, and every message keeps its old text.
- `with_prompt_context` runs once on every failure the parse returns. It sets `at.file` to the file label, puts the prompt's name in the `Name` fact for a body failure, and writes the prose `{file}:{line}: {message}. {default}`, or `{file}: {message}. {default}` with no location. An `Internal` failure has the default prose alone.
- `at` runs from the start to the end of a span at the site that raises it, `frontmatter_decode` takes the YAML line and column, and `compile_line` maps a Lua diagnostic's chunk line to the file line.
- A refused `uses:` list has the `Reason` fact `pack_duplicate` or `pack_reserved` and the Pack id as `Name`. A fence violation has the `Reason` fact `label`, `opener`, `closer`, or `lookalike`.
- The Engine's `Error::Parse` holds the universal error, and the `From` conversion from a parse failure is removed. `drive.rs` stamps its own refusal through `with_prompt_context` with the prompt's file.
- `PrepareError::Parse` holds the error in a `Box`. The workshop's `prompt_parse` maps the code to its wire code, and a `parse_lua` message now gains the `line N: ` prefix the other codes have.
- The facade no longer exports `ParseError` and `ParseErrorKind`, and the prompt crate drops its `thiserror` dependency.
- Tests of the removed cause and span mechanics are removed. The rest check the code, `at`, and facts, and new tests pin each code's message, location, facts, and prose.
`prepare` now fails with the universal `Error` instead of the bespoke `PrepareError`. A prompt that does not parse and an input that cannot be put in place keep their own failure and gain the run's `Run` fact, and `ended` gives `Harness::run_to_end` the outcome the run ended with. - `ended_kind` picks the run's kind from the code: the input's kind for `RequirementsUnmet` and the store codes, and `Parse` for every other code. Both end sites and `ended` use it. - `refused` makes a recorder's refusal `RecorderFailed` with the `Run` fact. A refusal of another code goes through `recorder_error`, so `ended` tells a recorder refusal from an ended run by its code alone. - The tests read the run from the `Run` fact through `run_of` and check the failure itself in place of the variants. A new test pins a refused parse event as `RecorderFailed` with the recorder's text and the run.
`Harness::run` now fails with the universal `Error` instead of the bespoke `HarnessError`, and its one failure is still a recorder that refuses a write. `unrecorded` builds it as `RecorderFailed`. - The message is `the run could not be recorded` followed by the refusal, so `display_chain` renders the same text as before. The error keeps the refusal's facts and adds the run's `Run` fact when the recorder issued one. - `harness-runner` and the `harness` facade no longer export `HarnessError`, and the workshop's `conversation-run` takes the universal error. - `thiserror` moves to the runner's dev-dependencies, which only the `display_chain` tests use. - The recorder tests check the code, the message prefix, and the run through `run_of` in place of the variant's fields.
A `{{ }}` substitution failure is now the universal `Error` under `Substitution` instead of the bespoke `SubstitutionError`, and the private `SubstErrorKind` becomes a `Reason` fact. `failure` builds every one from its site's detail text and the placeholder's byte offset.
- The `Reason` fact is the snake_case of the old kind, such as `missing_key`. The message keeps the documented form `{detail} [MissingKey at byte 4]`, with the kind name derived from the reason.
- The prose is the detail and the code's default text. The `serialize` failure appends its lookup failure to the message through `with_cause`, which now shows in the error's display text too.
- `Error::Substitution` holds the universal error, and the `From` conversion from `SubstitutionError` is removed. `substitute` maps the failure explicitly, because the engine's `From` for the universal error builds `Completion`.
- The tests check the `Reason` fact through `reason` in place of the kind. New tests pin a failure's code, message, prose, facts, and empty `at`, and that `substitute` yields `Error::Substitution`, never `Completion`.
The Engine's 26-variant `Error` is gone, and every Engine failure is now the universal `Error`. Small constructors such as `internal`, `tasks_live`, `out_of_scope_tool`, and `requirements_unmet` build each failure with its old display text as the message, a code, and facts in place of the variant's fields. - `run_error_kind` derives `RunErrorKind` from the code with one total match that gives every code the kind its failure had. `failure_message` and `outcome` keep `RunOutcome` text byte-identical, with the parse and internal location suffixes rebuilt from the `Name` fact and `at`. - The Lua crate drops the `ErrorValue` trait and its type parameter and works with the universal error. `error_kind`, `error_table_message`, and `error_fields` render the same Lua tables as before, and nothing new reaches the facade. - `into_universal` in the Lua crate converts the Lua crate's own `Error` and a raised table, and the Engine re-exports it and the constructors it shares. `ErrorCode::from_tag` maps a raised `store` table's `reason` back to its code. - A raised `cancelled` table that names its `task` is now that task's `TaskCancelled`, so a re-raised task cancellation fails the run as `Lua` the way the direct raise always did. Without a `task` it is still `Interrupted`, and the `RunOutcome::Cancelled` doc says so. - Tests of variant matches, `originating_error`, and source chains are removed or now match codes and facts. New tests walk every code's kind, round-trip every tag, and pin each constructor, the outcome suffixes, and the Lua tables of task, tasks, store, empty reply, and context failures.
The Lua crate now raises and reads back the universal `Error` instead of its own `Error` and `Raised`, and `QuotaExceeded`, `ListEditError`, `CompilerError`, the JSON-encode `Failure`, and `SharedSource` go with them. Each site builds its failure with the constructor that kept its message, code, and facts, so `into_universal` is removed.
- A universal error crosses the mlua boundary through `mlua::Error::external` and comes back by downcast. A quota refusal is a raised `LuaQuota` error, and a compiled program's static keeps a clone of its error.
- Every error table carries `code`, the error's tag, beside `kind`, and its `message` is the error's prose. `raised_from` reads a table back by its `code`, or by its `kind` when it has none, with the table's fields as facts.
- A failure raised from Lua has author prose: `{file}:{line}: {what failed}. {default}` with `at` on the mapped line, or `{file}: {what failed}. {default}` with no line. The prompt's file label reaches the VM through `SectionVmSetup.file` and `set_prompt_file`, and `LuaProgram::compile` takes it as a parameter.
- A table built from a universal error keeps that error in a weak-keyed registry, so an uncaught raise ends the run with the error's own message. A re-raised `lua` table maps by its message, so `RunOutcome` text is unchanged.
- A caught table of another kind that is re-raised after a suspension now keeps its own code. Tool, model, and context failures keep their remedy prose, and `Internal` keeps the default.
- Tests of the removed types and cause chains are removed, and table-text assertions now expect the exact prose. New tests pin the `code` field, the read-back of code, prose, and facts, author prose and `at` for runtime and compile failures, the quota boundary, and a tool failure's prose with no file or line.
The `ErrorCode` doc named its 8xx family after the Harness, which an Engine crate must not name. The family is now `caller`, the word Engine crates use for the code that supplies the recorder, the Plugins, and the run report. - The pre-push docs-claims scan passes with this change.
The supply-chain job's `cargo deny` check fails on the yanked `async-executor` 1.14.0. `Cargo.lock` now pins 1.14.1, and nothing else moves.
A new `AGENTS.md` rule tells Engine and Harness crates to report every failure as the universal `Error` and to add an `ErrorCode` variant instead of a new error type. The `engine`, `lua`, and `model-client` manifests under `crates/runvil-internal/` no longer declare `thiserror`, so a `thiserror` derive does not compile in these crates. - The rule names `VfsError` and `ParsePathReasonError` in `runvil-vfs` as the only exceptions. Test code can define its own error type to exercise `with_cause`. - The manifest change blocks only the derive. A hand-written `impl std::error::Error` still compiles, and this change adds no structural check for the rule.
This branch was successfully deployed
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.
No description provided.