1#![allow(unused_assignments)]
2use super::chained_error::ChainedError;
3use crate::{
4 ConfigError, FromValue, LabeledError, ParseError, Span, Spanned, Type, Value,
5 ast::Operator,
6 engine::{Stack, StateWorkingSet},
7 format_cli_error, record,
8};
9use generic::GenericError;
10use job::JobError;
11use miette::{Diagnostic, LabeledSpan, NamedSource};
12use nu_utils::location::Location;
13use serde::{Deserialize, Serialize};
14use std::{error::Error as StdError, num::NonZeroI32, sync::Arc};
15use thiserror::Error;
16
17pub mod bridge;
18pub mod generic;
19pub mod io;
20pub mod job;
21pub mod network;
22
23#[derive(Debug, Clone, Error, Diagnostic, PartialEq)]
27pub enum ShellError {
28 #[error("The '{op}' operator does not work on values of type '{unsupported}'.")]
30 #[diagnostic(code(nu::shell::operator_unsupported_type))]
31 OperatorUnsupportedType {
32 op: Operator,
33 unsupported: Type,
34 #[label = "does not support '{unsupported}'"]
35 op_span: Span,
36 #[label("{unsupported}")]
37 unsupported_span: Span,
38 #[help]
39 help: Option<&'static str>,
40 },
41
42 #[error("Types '{lhs}' and '{rhs}' are not compatible for the '{op}' operator.")]
44 #[diagnostic(code(nu::shell::operator_incompatible_types))]
45 OperatorIncompatibleTypes {
46 op: Operator,
47 lhs: Type,
48 rhs: Type,
49 #[label = "does not operate between '{lhs}' and '{rhs}'"]
50 op_span: Span,
51 #[label("{lhs}")]
52 lhs_span: Span,
53 #[label("{rhs}")]
54 rhs_span: Span,
55 #[help]
56 help: Option<&'static str>,
57 },
58
59 #[error("Operator overflow.")]
66 #[diagnostic(code(nu::shell::operator_overflow))]
67 OperatorOverflow {
68 msg: String,
69 #[label = "{msg}"]
70 span: Span,
71 #[help]
72 help: Option<String>,
73 },
74
75 #[error("Pipeline mismatch.")]
82 #[diagnostic(code(nu::shell::pipeline_mismatch))]
83 PipelineMismatch {
84 exp_input_type: String,
85 #[label("expected: {exp_input_type}")]
86 dst_span: Span,
87 #[label("value originates here")]
88 src_span: Span,
89 },
90
91 #[error("Input type not supported.")]
101 #[diagnostic(code(nu::shell::only_supports_this_input_type))]
102 OnlySupportsThisInputType {
103 exp_input_type: String,
104 wrong_type: String,
105 #[label("only {exp_input_type} input data is supported")]
106 dst_span: Span,
107 #[label("input type: {wrong_type}")]
108 src_span: Span,
109 },
110
111 #[error("Pipeline empty.")]
117 #[diagnostic(code(nu::shell::pipeline_mismatch))]
118 PipelineEmpty {
119 #[label("no input value was piped in")]
120 dst_span: Span,
121 },
122
123 #[error("Type mismatch.")]
130 #[diagnostic(code(nu::shell::type_mismatch))]
131 TypeMismatch {
132 err_message: String,
133 #[label = "{err_message}"]
134 span: Span,
135 },
136
137 #[error("Type mismatch")]
143 #[diagnostic(code(nu::shell::type_mismatch))]
144 RuntimeTypeMismatch {
145 expected: Type,
146 actual: Type,
147 #[label = "expected {expected}, but got {actual}"]
148 span: Span,
149 },
150
151 #[error("Invalid value")]
157 #[diagnostic(code(nu::shell::invalid_value))]
158 InvalidValue {
159 valid: String,
160 actual: String,
161 #[label = "expected {valid}, but got {actual}"]
162 span: Span,
163 },
164
165 #[error("Incorrect value.")]
171 #[diagnostic(code(nu::shell::incorrect_value))]
172 IncorrectValue {
173 msg: String,
174 #[label = "{msg}"]
175 val_span: Span,
176 #[label = "encountered here"]
177 call_span: Span,
178 },
179
180 #[error("Assignment operations require a variable.")]
186 #[diagnostic(code(nu::shell::assignment_requires_variable))]
187 AssignmentRequiresVar {
188 #[label = "needs to be a variable"]
189 lhs_span: Span,
190 },
191
192 #[error("Assignment to an immutable variable.")]
198 #[diagnostic(code(nu::shell::assignment_requires_mutable_variable))]
199 AssignmentRequiresMutableVar {
200 #[label = "needs to be a mutable variable"]
201 lhs_span: Span,
202 },
203
204 #[error("Unknown operator: {op_token}.")]
210 #[diagnostic(code(nu::shell::unknown_operator))]
211 UnknownOperator {
212 op_token: String,
213 #[label = "unknown operator"]
214 span: Span,
215 },
216
217 #[error("Missing parameter: {param_name}.")]
223 #[diagnostic(code(nu::shell::missing_parameter))]
224 MissingParameter {
225 param_name: String,
226 #[label = "missing parameter: {param_name}"]
227 span: Span,
228 },
229
230 #[error("Incompatible parameters.")]
236 #[diagnostic(code(nu::shell::incompatible_parameters))]
237 IncompatibleParameters {
238 left_message: String,
239 #[label("{left_message}")]
241 left_span: Span,
242 right_message: String,
243 #[label("{right_message}")]
244 right_span: Span,
245 },
246
247 #[error("Delimiter error")]
253 #[diagnostic(code(nu::shell::delimiter_error))]
254 DelimiterError {
255 msg: String,
256 #[label("{msg}")]
257 span: Span,
258 },
259
260 #[error("Incompatible parameters.")]
268 #[diagnostic(code(nu::shell::incompatible_parameters))]
269 IncompatibleParametersSingle {
270 msg: String,
271 #[label = "{msg}"]
272 span: Span,
273 },
274
275 #[error("Running external commands not supported")]
281 #[diagnostic(code(nu::shell::external_commands))]
282 ExternalNotSupported {
283 #[label = "external not supported"]
284 span: Span,
285 },
286
287 #[error("Invalid Probability.")]
294 #[diagnostic(code(nu::shell::invalid_probability))]
295 InvalidProbability {
296 #[label = "invalid probability: must be between 0 and 1"]
297 span: Span,
298 },
299
300 #[error("Invalid range {left_flank}..{right_flank}")]
306 #[diagnostic(code(nu::shell::invalid_range))]
307 InvalidRange {
308 left_flank: String,
309 right_flank: String,
310 #[label = "expected a valid range"]
311 span: Span,
312 },
313
314 #[error("Nushell failed: {msg}.")]
320 #[diagnostic(
321 code(nu::shell::nushell_failed),
322 help(
323 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
324 )
325 )]
326 NushellFailed { msg: String },
328
329 #[error("Nushell failed: {msg}.")]
335 #[diagnostic(
336 code(nu::shell::nushell_failed_spanned),
337 help(
338 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
339 )
340 )]
341 NushellFailedSpanned {
343 msg: String,
344 label: String,
345 #[label = "{label}"]
346 span: Span,
347 },
348
349 #[error("Nushell failed: {msg}.")]
355 #[diagnostic(code(nu::shell::nushell_failed_help))]
356 NushellFailedHelp {
358 msg: String,
359 #[help]
360 help: String,
361 },
362
363 #[error("Variable not found")]
369 #[diagnostic(code(nu::shell::variable_not_found))]
370 VariableNotFoundAtRuntime {
371 #[label = "variable not found"]
372 span: Span,
373 },
374
375 #[error("Environment variable '{envvar_name}' not found")]
381 #[diagnostic(code(nu::shell::env_variable_not_found))]
382 EnvVarNotFoundAtRuntime {
383 envvar_name: String,
384 #[label = "environment variable not found"]
385 span: Span,
386 },
387
388 #[error("Module '{mod_name}' not found")]
394 #[diagnostic(code(nu::shell::module_not_found))]
395 ModuleNotFoundAtRuntime {
396 mod_name: String,
397 #[label = "module not found"]
398 span: Span,
399 },
400
401 #[error("Overlay '{overlay_name}' not found")]
407 #[diagnostic(code(nu::shell::overlay_not_found))]
408 OverlayNotFoundAtRuntime {
409 overlay_name: String,
410 #[label = "overlay not found"]
411 span: Span,
412 },
413
414 #[error("Not found.")]
420 #[diagnostic(code(nu::parser::not_found))]
421 NotFound {
422 #[label = "did not find anything under this name"]
423 span: Span,
424 },
425
426 #[error("Can't convert to {to_type}.")]
432 #[diagnostic(code(nu::shell::cant_convert))]
433 CantConvert {
434 to_type: String,
435 from_type: String,
436 #[label("can't convert {from_type} to {to_type}")]
437 span: Span,
438 #[help]
439 help: Option<String>,
440 },
441
442 #[error("Can't convert {from_type} to the specified unit.")]
448 #[diagnostic(code(nu::shell::cant_convert_value_to_unit))]
449 CantConvertToUnit {
450 to_type: String,
451 from_type: String,
452 #[label("can't convert {from_type} to {to_type}")]
453 span: Span,
454 #[label("conversion originates here")]
455 unit_span: Span,
456 #[help]
457 help: Option<String>,
458 },
459
460 #[error("'{envvar_name}' is not representable as a string.")]
466 #[diagnostic(
467 code(nu::shell::env_var_not_a_string),
468 help(
469 "The '{envvar_name}' environment variable must be a string or be convertible to a string.
470 Either make sure '{envvar_name}' is a string, or add a 'to_string' entry for it in ENV_CONVERSIONS."
471 )
472 )]
473 EnvVarNotAString {
474 envvar_name: String,
475 #[label("value not representable as a string")]
476 span: Span,
477 },
478
479 #[error("{envvar_name} cannot be set manually.")]
485 #[diagnostic(
486 code(nu::shell::automatic_env_var_set_manually),
487 help(
488 "The environment variable '{envvar_name}' is set automatically by Nushell and cannot be set manually."
489 )
490 )]
491 AutomaticEnvVarSetManually {
492 envvar_name: String,
493 #[label("cannot set '{envvar_name}' manually")]
494 span: Span,
495 },
496
497 #[error("Cannot replace environment.")]
504 #[diagnostic(
505 code(nu::shell::cannot_replace_env),
506 help("Assigning a value to '$env' is not allowed.")
507 )]
508 CannotReplaceEnv {
509 #[label("setting '$env' not allowed")]
510 span: Span,
511 },
512
513 #[error("Division by zero.")]
519 #[diagnostic(code(nu::shell::division_by_zero))]
520 DivisionByZero {
521 #[label("division by zero")]
522 span: Span,
523 },
524
525 #[error("Can't convert range to countable values")]
533 #[diagnostic(code(nu::shell::range_to_countable))]
534 CannotCreateRange {
535 #[label = "can't convert to countable values"]
536 span: Span,
537 },
538
539 #[error("Row number too large (max: {max_idx}).")]
545 #[diagnostic(code(nu::shell::access_beyond_end))]
546 AccessBeyondEnd {
547 max_idx: usize,
548 #[label = "index too large (max: {max_idx})"]
549 span: Span,
550 },
551
552 #[error("Inserted at wrong row number (should be {available_idx}).")]
558 #[diagnostic(code(nu::shell::access_beyond_end))]
559 InsertAfterNextFreeIndex {
560 available_idx: usize,
561 #[label = "can't insert at index (the next available index is {available_idx})"]
562 span: Span,
563 },
564
565 #[error("Row number too large (empty content).")]
571 #[diagnostic(code(nu::shell::access_beyond_end))]
572 AccessEmptyContent {
573 #[label = "index too large (empty content)"]
574 span: Span,
575 },
576
577 #[error("Row number too large.")]
584 #[diagnostic(code(nu::shell::access_beyond_end_of_stream))]
585 AccessBeyondEndOfStream {
586 #[label = "index too large"]
587 span: Span,
588 },
589
590 #[error("Data cannot be accessed with a cell path")]
596 #[diagnostic(code(nu::shell::incompatible_path_access))]
597 IncompatiblePathAccess {
598 type_name: String,
599 #[label("{type_name} doesn't support cell paths")]
600 span: Span,
601 },
602
603 #[error("Cannot find column '{col_name}'")]
609 #[diagnostic(code(nu::shell::column_not_found))]
610 CantFindColumn {
611 col_name: String,
612 #[label = "cannot find column '{col_name}'"]
613 span: Option<Span>,
614 #[label = "value originates here"]
615 src_span: Span,
616 },
617
618 #[error("Column already exists")]
624 #[diagnostic(code(nu::shell::column_already_exists))]
625 ColumnAlreadyExists {
626 col_name: String,
627 #[label = "column '{col_name}' already exists"]
628 span: Span,
629 #[label = "value originates here"]
630 src_span: Span,
631 },
632
633 #[error("Not a list value")]
639 #[diagnostic(code(nu::shell::not_a_list))]
640 NotAList {
641 #[label = "value not a list"]
642 dst_span: Span,
643 #[label = "value originates here"]
644 src_span: Span,
645 },
646
647 #[error("Record field or table column used twice: {col_name}")]
653 #[diagnostic(code(nu::shell::column_defined_twice))]
654 ColumnDefinedTwice {
655 col_name: String,
656 #[label = "field redefined here"]
657 second_use: Span,
658 #[label = "field first defined here"]
659 first_use: Span,
660 },
661
662 #[error("Attempted to create a record from different number of columns and values")]
668 #[diagnostic(code(nu::shell::record_cols_vals_mismatch))]
669 RecordColsValsMismatch {
670 #[label = "problematic value"]
671 bad_value: Span,
672 #[label = "attempted to create the record here"]
673 creation_site: Span,
674 },
675
676 #[error("Failed to detect columns")]
682 #[diagnostic(code(nu::shell::failed_to_detect_columns))]
683 ColumnDetectionFailure {
684 #[label = "value coming from here"]
685 bad_value: Span,
686 #[label = "tried to detect columns here"]
687 failure_site: Span,
688 },
689
690 #[error("Relative range values cannot be used with streams that don't have a known length")]
696 #[diagnostic(code(nu::shell::relative_range_on_infinite_stream))]
697 RelativeRangeOnInfiniteStream {
698 #[label = "Relative range values cannot be used with streams that don't have a known length"]
699 span: Span,
700 },
701
702 #[error("External command failed")]
708 #[diagnostic(code(nu::shell::external_command), help("{help}"))]
709 ExternalCommand {
710 label: String,
711 help: String,
712 #[label("{label}")]
713 span: Span,
714 },
715
716 #[error("External command had a non-zero exit code")]
722 #[diagnostic(code(nu::shell::non_zero_exit_code))]
723 NonZeroExitCode {
724 exit_code: NonZeroI32,
725 #[label("exited with code {exit_code}")]
726 span: Span,
727 },
728
729 #[cfg(unix)]
730 #[error("External command was terminated by a signal")]
736 #[diagnostic(code(nu::shell::terminated_by_signal))]
737 TerminatedBySignal {
738 signal_name: String,
739 signal: i32,
740 #[label("terminated by {signal_name} ({signal})")]
741 span: Span,
742 },
743
744 #[cfg(unix)]
745 #[error("External command core dumped")]
751 #[diagnostic(code(nu::shell::core_dumped))]
752 CoreDumped {
753 signal_name: String,
754 signal: i32,
755 #[label("core dumped with {signal_name} ({signal})")]
756 span: Span,
757 },
758
759 #[error("Unsupported input")]
765 #[diagnostic(code(nu::shell::unsupported_input))]
766 UnsupportedInput {
767 msg: String,
768 input: String,
769 #[label("{msg}")]
770 msg_span: Span,
771 #[label("{input}")]
772 input_span: Span,
773 },
774
775 #[error("Unable to parse datetime: [{msg}].")]
790 #[diagnostic(
791 code(nu::shell::datetime_parse_error),
792 help(
793 r#"Examples of supported inputs:
794 * "5 pm"
795 * "2020/12/4"
796 * "2020.12.04 22:10 +2"
797 * "2020-04-12 22:10:57 +02:00"
798 * "2020-04-12T22:10:57.213231+02:00"
799 * "Tue, 1 Jul 2003 10:52:37 +0200""#
800 )
801 )]
802 DatetimeParseError {
803 msg: String,
804 #[label("datetime parsing failed")]
805 span: Span,
806 },
807
808 #[error("Network failure")]
814 #[diagnostic(code(nu::shell::network_failure))]
815 NetworkFailure {
816 msg: String,
817 #[label("{msg}")]
818 span: Span,
819 },
820
821 #[error(transparent)]
822 #[diagnostic(transparent)]
823 Network(#[from] network::NetworkError),
824
825 #[error("Command not found")]
831 #[diagnostic(code(nu::shell::command_not_found))]
832 CommandNotFound {
833 #[label("command not found")]
834 span: Span,
835 },
836
837 #[error("Alias not found")]
843 #[diagnostic(code(nu::shell::alias_not_found))]
844 AliasNotFound {
845 #[label("alias not found")]
846 span: Span,
847 },
848
849 #[error("The registered plugin data for `{plugin_name}` is invalid")]
855 #[diagnostic(code(nu::shell::plugin_registry_data_invalid))]
856 PluginRegistryDataInvalid {
857 plugin_name: String,
858 #[label("plugin `{plugin_name}` loaded here")]
859 span: Option<Span>,
860 #[help(
861 "the format in the plugin registry file is not compatible with this version of Nushell.\n\nTry adding the plugin again with `{}`"
862 )]
863 add_command: String,
864 },
865
866 #[error("Plugin failed to load: {msg}")]
872 #[diagnostic(code(nu::shell::plugin_failed_to_load))]
873 PluginFailedToLoad { msg: String },
874
875 #[error("Plugin failed to encode: {msg}")]
881 #[diagnostic(code(nu::shell::plugin_failed_to_encode))]
882 PluginFailedToEncode { msg: String },
883
884 #[error("Plugin failed to decode: {msg}")]
890 #[diagnostic(code(nu::shell::plugin_failed_to_decode))]
891 PluginFailedToDecode { msg: String },
892
893 #[error("Custom value `{name}` cannot be sent to plugin")]
900 #[diagnostic(code(nu::shell::custom_value_incorrect_for_plugin))]
901 CustomValueIncorrectForPlugin {
902 name: String,
903 #[label("the `{dest_plugin}` plugin does not support this kind of value")]
904 span: Span,
905 dest_plugin: String,
906 #[help("this value came from the `{}` plugin")]
907 src_plugin: Option<String>,
908 },
909
910 #[error("Custom value failed to encode")]
917 #[diagnostic(code(nu::shell::custom_value_failed_to_encode))]
918 CustomValueFailedToEncode {
919 msg: String,
920 #[label("{msg}")]
921 span: Span,
922 },
923
924 #[error("Custom value failed to decode")]
931 #[diagnostic(code(nu::shell::custom_value_failed_to_decode))]
932 #[diagnostic(help("the plugin may have been updated and no longer support this custom value"))]
933 CustomValueFailedToDecode {
934 msg: String,
935 #[label("{msg}")]
936 span: Span,
937 },
938
939 #[error(transparent)]
945 #[diagnostic(transparent)]
946 Io(#[from] io::IoError),
947
948 #[error("Name not found")]
954 #[diagnostic(code(nu::shell::name_not_found))]
955 DidYouMean {
956 suggestion: String,
957 #[label("did you mean '{suggestion}'?")]
958 span: Span,
959 },
960
961 #[error("{msg}")]
967 #[diagnostic(code(nu::shell::did_you_mean_custom))]
968 DidYouMeanCustom {
969 msg: String,
970 suggestion: String,
971 #[label("did you mean '{suggestion}'?")]
972 span: Span,
973 },
974
975 #[error("Non-UTF8 string")]
981 #[diagnostic(
982 code(nu::parser::non_utf8),
983 help("see `decode` for handling character sets other than UTF-8")
984 )]
985 NonUtf8 {
986 #[label("non-UTF8 string")]
987 span: Span,
988 },
989
990 #[error("Non-UTF8 string")]
996 #[diagnostic(
997 code(nu::parser::non_utf8_custom),
998 help("see `decode` for handling character sets other than UTF-8")
999 )]
1000 NonUtf8Custom {
1001 msg: String,
1002 #[label("{msg}")]
1003 span: Span,
1004 },
1005
1006 #[error("Encountered {} error(s) when updating config", errors.len())]
1012 #[diagnostic(code(nu::shell::invalid_config))]
1013 InvalidConfig {
1014 #[related]
1015 errors: Vec<ConfigError>,
1016 },
1017
1018 #[error("Value is missing a required '{column}' column")]
1024 #[diagnostic(code(nu::shell::missing_required_column))]
1025 MissingRequiredColumn {
1026 column: &'static str,
1027 #[label("has no '{column}' column")]
1028 span: Span,
1029 },
1030
1031 #[error("Negative value passed when positive one is required")]
1037 #[diagnostic(code(nu::shell::needs_positive_value))]
1038 NeedsPositiveValue {
1039 #[label("use a positive value")]
1040 span: Span,
1041 },
1042
1043 #[error("{error}")]
1045 #[diagnostic(code(nu::shell::error))]
1046 #[deprecated(since = "0.111.1", note = "use `ShellError::Generic` instead")]
1047 GenericError {
1048 error: String,
1049 msg: String,
1050 #[label("{msg}")]
1051 span: Option<Span>,
1052 #[help]
1053 help: Option<String>,
1054 #[related]
1055 inner: Vec<ShellError>,
1056 },
1057
1058 #[error(transparent)]
1060 #[diagnostic(transparent)]
1061 Generic(#[from] generic::GenericError),
1062
1063 #[error("{error}")]
1065 #[diagnostic(code(nu::shell::outsidespan))]
1066 OutsideSpannedLabeledError {
1067 #[source_code]
1068 src: String,
1069 error: String,
1070 msg: String,
1071 #[label("{msg}")]
1072 span: Span,
1073 },
1074
1075 #[error("{msg}")]
1078 #[diagnostic(code(nu::shell::outside), url("{url}"))]
1079 OutsideSource {
1080 #[source_code]
1081 src: NamedSource<String>,
1082 msg: String,
1083 url: String,
1084 #[help]
1085 help: Option<String>,
1086 #[label(collection, "")]
1088 labels: Vec<LabeledSpan>,
1089 #[related]
1090 inner: Vec<ShellError>,
1091 },
1092
1093 #[error("{msg}")]
1096 #[diagnostic(code(nu::shell::outside))]
1097 OutsideSourceNoUrl {
1098 #[source_code]
1099 src: NamedSource<String>,
1100 msg: String,
1101 #[help]
1102 help: Option<String>,
1103 #[label(collection, "")]
1105 labels: Vec<LabeledSpan>,
1106 #[related]
1107 inner: Vec<ShellError>,
1108 },
1109
1110 #[error(transparent)]
1112 #[diagnostic(transparent)]
1113 LabeledError(#[from] Box<super::LabeledError>),
1114
1115 #[error("Removed command: {removed}")]
1121 #[diagnostic(code(nu::shell::removed_command))]
1122 RemovedCommand {
1123 removed: String,
1124 replacement: String,
1125 #[label("'{removed}' has been removed from Nushell. Please use '{replacement}' instead.")]
1126 span: Span,
1127 },
1128
1129 #[error("Eval block failed with pipeline input")]
1132 #[diagnostic(code(nu::shell::eval_block_with_input))]
1133 EvalBlockWithInput {
1134 #[label("source value")]
1135 span: Span,
1136 #[related]
1137 sources: Vec<ShellError>,
1138 },
1139
1140 #[error("Break used outside of loop")]
1142 Break {
1143 #[label("used outside of loop")]
1144 span: Span,
1145 },
1146
1147 #[error("Continue used outside of loop")]
1149 Continue {
1150 #[label("used outside of loop")]
1151 span: Span,
1152 },
1153
1154 #[error("Return used outside of custom command or closure")]
1156 Return {
1157 #[label("used outside of custom command or closure")]
1158 span: Span,
1159 value: Box<Value>,
1160 },
1161
1162 #[error("Exit doesn't catch internally")]
1164 #[diagnostic(
1165 code(nu::shell::exit),
1166 help(
1167 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
1168 )
1169 )]
1170 Exit { code: i32 },
1171
1172 #[error("Recursion limit ({recursion_limit}) reached")]
1178 #[diagnostic(code(nu::shell::recursion_limit_reached))]
1179 RecursionLimitReached {
1180 recursion_limit: u64,
1181 #[label("This called itself too many times")]
1182 span: Option<Span>,
1183 },
1184
1185 #[error("Operation interrupted")]
1187 Interrupted {
1188 #[label("This operation was interrupted")]
1189 span: Span,
1190 },
1191
1192 #[error("Match guard not bool")]
1195 #[diagnostic(
1196 code(nu::shell::match_guard_not_bool),
1197 help("Match guards should evaluate to a boolean")
1198 )]
1199 MatchGuardNotBool {
1200 #[label("not a boolean expression")]
1201 span: Span,
1202 },
1203
1204 #[error("Missing const eval implementation")]
1209 #[diagnostic(
1210 code(nu::shell::missing_const_eval_implementation),
1211 help(
1212 "The command lacks an implementation for constant evaluation. \
1213This is an internal Nushell error, please file an issue https://github.com/nushell/nushell/issues."
1214 )
1215 )]
1216 MissingConstEvalImpl {
1217 #[label("command lacks constant implementation")]
1218 span: Span,
1219 },
1220
1221 #[error("Found parsing error in expression.")]
1229 #[diagnostic(
1230 code(nu::shell::parse_error_in_constant),
1231 help(
1232 "This expression is supposed to be evaluated into a constant, which means error-free."
1233 )
1234 )]
1235 ParseErrorInConstant {
1236 #[label("Parsing error detected in expression")]
1237 span: Span,
1238 },
1239
1240 #[error("Not a constant.")]
1246 #[diagnostic(
1247 code(nu::shell::not_a_constant),
1248 help(
1249 "Only a subset of expressions are allowed constants during parsing. Try using the 'const' command or typing the value literally."
1250 )
1251 )]
1252 NotAConstant {
1253 #[label("Value is not a parse-time constant")]
1254 span: Span,
1255 },
1256
1257 #[error("Not a const command.")]
1264 #[diagnostic(
1265 code(nu::shell::not_a_const_command),
1266 help("Only a subset of builtin commands can run at parse time.")
1267 )]
1268 NotAConstCommand {
1269 #[label("This command cannot run at parse time.")]
1270 span: Span,
1271 },
1272
1273 #[error("Help message not a constant.")]
1279 #[diagnostic(
1280 code(nu::shell::not_a_const_help),
1281 help("Help messages are currently not supported to be constants.")
1282 )]
1283 NotAConstHelp {
1284 #[label("This command cannot run at parse time.")]
1285 span: Span,
1286 },
1287
1288 #[error("{deprecation_type} deprecated.")]
1289 #[diagnostic(code(nu::shell::deprecated), severity(Warning))]
1290 DeprecationWarning {
1291 deprecation_type: &'static str,
1292 suggestion: String,
1293 #[label("{suggestion}")]
1294 span: Span,
1295 #[help]
1296 help: Option<&'static str>,
1297 },
1298
1299 #[error("Invalid glob pattern")]
1305 #[diagnostic(
1306 code(nu::shell::invalid_glob_pattern),
1307 help("Refer to xxx for help on nushell glob patterns.")
1308 )]
1309 InvalidGlobPattern {
1310 msg: String,
1311 #[label("{msg}")]
1312 span: Span,
1313 },
1314
1315 #[error("Invalid unit")]
1321 #[diagnostic(
1322 code(nu::shell::invalid_unit),
1323 help("Supported units are: {supported_units}")
1324 )]
1325 InvalidUnit {
1326 supported_units: String,
1327 #[label("encountered here")]
1328 span: Span,
1329 },
1330
1331 #[error("Not a list")]
1337 #[diagnostic(
1338 code(nu::shell::cannot_spread_as_list),
1339 help(
1340 "Only lists can be spread inside lists and command calls. Try converting the value to a list before spreading."
1341 )
1342 )]
1343 CannotSpreadAsList {
1344 #[label = "cannot spread value"]
1345 span: Span,
1346 },
1347
1348 #[error("Not a record")]
1354 #[diagnostic(
1355 code(nu::shell::cannot_spread_as_record),
1356 help(
1357 "Only records can be spread inside records. Try converting the value to a record before spreading."
1358 )
1359 )]
1360 CannotSpreadAsRecord {
1361 #[label = "cannot spread value"]
1362 span: Span,
1363 },
1364
1365 #[error("Lists are not automatically spread when calling external commands")]
1371 #[diagnostic(
1372 code(nu::shell::cannot_pass_list_to_external),
1373 help("Either convert the list to a string or use the spread operator, like so: ...{arg}")
1374 )]
1375 CannotPassListToExternal {
1376 arg: String,
1377 #[label = "Spread operator (...) is necessary to spread lists"]
1378 span: Span,
1379 },
1380
1381 #[error(
1387 "The selected range {left_flank}..{right_flank} is out of the bounds of the provided input"
1388 )]
1389 #[diagnostic(code(nu::shell::out_of_bounds))]
1390 OutOfBounds {
1391 left_flank: String,
1392 right_flank: String,
1393 #[label = "byte index is not a char boundary or is out of bounds of the input"]
1394 span: Span,
1395 },
1396
1397 #[error("The config directory could not be found")]
1399 #[diagnostic(
1400 code(nu::shell::config_dir_not_found),
1401 help(
1402 r#"On Linux, this would be $XDG_CONFIG_HOME or $HOME/.config.
1403On MacOS, this would be `$HOME/Library/Application Support`.
1404On Windows, this would be %USERPROFILE%\AppData\Roaming"#
1405 )
1406 )]
1407 ConfigDirNotFound {
1408 #[label = "Could not find config directory"]
1409 span: Span,
1410 },
1411
1412 #[error(
1414 "$env.XDG_CONFIG_HOME ({xdg}) is invalid, using default config directory instead: {default}"
1415 )]
1416 #[diagnostic(
1417 code(nu::shell::xdg_config_home_invalid),
1418 help("Set XDG_CONFIG_HOME to an absolute path, or set it to an empty string to ignore it")
1419 )]
1420 InvalidXdgConfig { xdg: String, default: String },
1421
1422 #[error("IR evaluation error: {msg}")]
1429 #[diagnostic(
1430 code(nu::shell::ir_eval_error),
1431 help(
1432 "this is a bug, please report it at https://github.com/nushell/nushell/issues/new along with the code you were running if able"
1433 )
1434 )]
1435 IrEvalError {
1436 msg: String,
1437 #[label = "while running this code"]
1438 span: Option<Span>,
1439 },
1440
1441 #[error("OS feature is disabled: {msg}")]
1442 #[diagnostic(
1443 code(nu::shell::os_disabled),
1444 help("You're probably running outside an OS like a browser, we cannot support this")
1445 )]
1446 DisabledOsSupport {
1447 msg: String,
1448 #[label = "while running this code"]
1449 span: Span,
1450 },
1451
1452 #[error(transparent)]
1453 #[diagnostic(transparent)]
1454 Job(#[from] JobError),
1455
1456 #[error(transparent)]
1457 #[diagnostic(transparent)]
1458 ChainedError(ChainedError),
1459}
1460
1461impl ShellError {
1462 pub fn external_exit_code(&self) -> Option<Spanned<i32>> {
1463 let (item, span) = match *self {
1464 Self::NonZeroExitCode { exit_code, span } => (exit_code.into(), span),
1465 #[cfg(unix)]
1466 Self::TerminatedBySignal { signal, span, .. }
1467 | Self::CoreDumped { signal, span, .. } => (-signal, span),
1468 _ => return None,
1469 };
1470 Some(Spanned { item, span })
1471 }
1472
1473 pub fn exit_code(&self) -> Option<i32> {
1474 match self {
1475 Self::Return { .. } | Self::Break { .. } | Self::Continue { .. } => None,
1476 _ => self.external_exit_code().map(|e| e.item).or(Some(1)),
1477 }
1478 }
1479
1480 pub fn into_full_value(
1481 self,
1482 working_set: &StateWorkingSet,
1483 stack: &Stack,
1484 span: Span,
1485 ) -> Value {
1486 let exit_code = self.external_exit_code();
1487
1488 let mut record = record! {
1489 "msg" => Value::string(self.to_string(), span),
1490 "debug" => Value::string(format!("{self:?}"), span),
1491 "raw" => Value::error(self.clone(), span),
1492 "rendered" => Value::string(format_cli_error(Some(stack), working_set, &self, Some("nu::shell::error")), span),
1493 "json" => Value::string(serde_json::to_string(&self).expect("Could not serialize error"), span),
1494 };
1495
1496 if let Some(code) = exit_code {
1497 record.push("exit_code", Value::int(code.item.into(), code.span));
1498 }
1499
1500 Value::record(record, span)
1501 }
1502
1503 pub fn wrap(self, working_set: &StateWorkingSet, span: Span) -> ParseError {
1505 let msg = format_cli_error(None, working_set, &self, None);
1506 ParseError::LabeledError(
1507 msg,
1508 "Encountered error during parse-time evaluation".into(),
1509 span,
1510 )
1511 }
1512
1513 pub fn into_chained(self, span: Span) -> Self {
1515 Self::ChainedError(match self {
1516 Self::ChainedError(inner) => ChainedError::new_chained(inner, span),
1517 other => {
1518 let error = other.clone();
1521 let mut now = ChainedError::new(other, span);
1522 if let Some(related) = error.related() {
1523 let mapped = related
1524 .map(|s| {
1525 let shellerror: Self = Self::from_diagnostic(s);
1526 shellerror
1527 })
1528 .collect::<Vec<_>>();
1529 if !mapped.is_empty() {
1530 now.sources = [now.sources, mapped].concat();
1531 };
1532 }
1533 now
1534 }
1535 })
1536 }
1537
1538 pub fn from_diagnostic(diag: &(impl miette::Diagnostic + ?Sized)) -> Self {
1539 Self::LabeledError(LabeledError::from_diagnostic(diag).into())
1540 }
1541}
1542
1543impl FromValue for ShellError {
1544 fn from_value(v: Value) -> Result<Self, ShellError> {
1545 let from_type = v.get_type();
1546 match v {
1547 Value::Error { error, .. } => Ok(*error),
1548 Value::Record {
1550 val, internal_span, ..
1551 } => Self::from_value(
1552 (*val)
1553 .get("raw")
1554 .ok_or(ShellError::CantConvert {
1555 to_type: Self::expected_type().to_string(),
1556 from_type: from_type.to_string(),
1557 span: internal_span,
1558 help: None,
1559 })?
1560 .clone(),
1561 ),
1562 Value::Nothing { internal_span } => Ok(Self::Generic(GenericError::new(
1563 "error",
1564 "is nothing",
1565 internal_span,
1566 ))),
1567 _ => Err(ShellError::CantConvert {
1568 to_type: Self::expected_type().to_string(),
1569 from_type: v.get_type().to_string(),
1570 span: v.span(),
1571 help: None,
1572 }),
1573 }
1574 }
1575}
1576
1577impl From<Box<dyn std::error::Error>> for ShellError {
1578 fn from(error: Box<dyn std::error::Error>) -> ShellError {
1579 ShellError::Generic(GenericError::new_internal(
1580 format!("{error:?}"),
1581 error.to_string(),
1582 ))
1583 }
1584}
1585
1586impl From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
1587 fn from(error: Box<dyn std::error::Error + Send + Sync>) -> ShellError {
1588 ShellError::Generic(GenericError::new_internal(
1589 format!("{error:?}"),
1590 error.to_string(),
1591 ))
1592 }
1593}
1594
1595impl From<super::LabeledError> for ShellError {
1596 fn from(error: super::LabeledError) -> Self {
1597 ShellError::LabeledError(Box::new(error))
1598 }
1599}
1600
1601impl Serialize for ShellError {
1603 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1604 where
1605 S: serde::Serializer,
1606 {
1607 LabeledError::from_diagnostic(self).serialize(serializer)
1608 }
1609}
1610
1611impl<'de> Deserialize<'de> for ShellError {
1614 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1615 where
1616 D: serde::Deserializer<'de>,
1617 {
1618 LabeledError::deserialize(deserializer).map(ShellError::from)
1619 }
1620}
1621
1622#[test]
1623fn shell_error_serialize_roundtrip() {
1624 let original_error = ShellError::CantConvert {
1627 span: Span::new(100, 200),
1628 to_type: "Foo".into(),
1629 from_type: "Bar".into(),
1630 help: Some("this is a test".into()),
1631 };
1632 println!("orig_error = {original_error:#?}");
1633
1634 let serialized =
1635 serde_json::to_string_pretty(&original_error).expect("serde_json::to_string_pretty failed");
1636 println!("serialized = {serialized}");
1637
1638 let deserialized: ShellError =
1639 serde_json::from_str(&serialized).expect("serde_json::from_str failed");
1640 println!("deserialized = {deserialized:#?}");
1641
1642 assert_eq!(original_error.to_string(), deserialized.to_string());
1645
1646 assert_eq!(
1647 original_error.code().map(|c| c.to_string()),
1648 deserialized.code().map(|c| c.to_string())
1649 );
1650
1651 let orig_labels = original_error
1652 .labels()
1653 .into_iter()
1654 .flatten()
1655 .collect::<Vec<_>>();
1656 let deser_labels = deserialized
1657 .labels()
1658 .into_iter()
1659 .flatten()
1660 .collect::<Vec<_>>();
1661
1662 assert_eq!(orig_labels, deser_labels);
1663
1664 assert_eq!(
1665 original_error.help().map(|c| c.to_string()),
1666 deserialized.help().map(|c| c.to_string())
1667 );
1668}
1669
1670#[derive(Debug, Clone, Eq, PartialEq)]
1676pub enum ErrorSite {
1677 Span(Span),
1679
1680 Location(String),
1684}
1685
1686impl From<Span> for ErrorSite {
1687 fn from(span: Span) -> Self {
1688 Self::Span(span)
1689 }
1690}
1691
1692impl From<Location> for ErrorSite {
1693 fn from(location: Location) -> Self {
1694 Self::Location(location.to_string())
1695 }
1696}
1697
1698#[derive(Debug, Error, Clone, Diagnostic)]
1700#[error(transparent)]
1701pub struct ErrorSource(Arc<dyn StdError + Send + Sync>);
1702
1703impl PartialEq for ErrorSource {
1704 fn eq(&self, other: &Self) -> bool {
1705 self.0.to_string() == other.0.to_string()
1707 }
1708}
1709
1710#[cfg(test)]
1711mod test {
1712 use super::*;
1713
1714 impl From<std::io::Error> for ShellError {
1715 fn from(_: std::io::Error) -> ShellError {
1716 unimplemented!(
1717 "This implementation is defined in the test module to ensure no other implementation exists."
1718 )
1719 }
1720 }
1721
1722 impl From<Spanned<std::io::Error>> for ShellError {
1723 fn from(_: Spanned<std::io::Error>) -> Self {
1724 unimplemented!(
1725 "This implementation is defined in the test module to ensure no other implementation exists."
1726 )
1727 }
1728 }
1729
1730 impl From<ShellError> for std::io::Error {
1731 fn from(_: ShellError) -> Self {
1732 unimplemented!(
1733 "This implementation is defined in the test module to ensure no other implementation exists."
1734 )
1735 }
1736 }
1737}