1use super::chained_error::ChainedError;
2use crate::{
3 ConfigError, FromValue, LabeledError, ParseError, Span, Spanned, Type, Value,
4 ast::Operator,
5 engine::{Stack, StateWorkingSet},
6 format_cli_error, record,
7};
8use job::JobError;
9use miette::{Diagnostic, LabeledSpan, NamedSource};
10use serde::{Deserialize, Serialize};
11use std::num::NonZeroI32;
12use thiserror::Error;
13
14pub mod bridge;
15pub mod io;
16pub mod job;
17pub mod location;
18pub mod network;
19
20#[derive(Debug, Clone, Error, Diagnostic, PartialEq)]
24pub enum ShellError {
25 #[error("The '{op}' operator does not work on values of type '{unsupported}'.")]
27 #[diagnostic(code(nu::shell::operator_unsupported_type))]
28 OperatorUnsupportedType {
29 op: Operator,
30 unsupported: Type,
31 #[label = "does not support '{unsupported}'"]
32 op_span: Span,
33 #[label("{unsupported}")]
34 unsupported_span: Span,
35 #[help]
36 help: Option<&'static str>,
37 },
38
39 #[error("Types '{lhs}' and '{rhs}' are not compatible for the '{op}' operator.")]
41 #[diagnostic(code(nu::shell::operator_incompatible_types))]
42 OperatorIncompatibleTypes {
43 op: Operator,
44 lhs: Type,
45 rhs: Type,
46 #[label = "does not operate between '{lhs}' and '{rhs}'"]
47 op_span: Span,
48 #[label("{lhs}")]
49 lhs_span: Span,
50 #[label("{rhs}")]
51 rhs_span: Span,
52 #[help]
53 help: Option<&'static str>,
54 },
55
56 #[error("Operator overflow.")]
63 #[diagnostic(code(nu::shell::operator_overflow))]
64 OperatorOverflow {
65 msg: String,
66 #[label = "{msg}"]
67 span: Span,
68 #[help]
69 help: Option<String>,
70 },
71
72 #[error("Pipeline mismatch.")]
79 #[diagnostic(code(nu::shell::pipeline_mismatch))]
80 PipelineMismatch {
81 exp_input_type: String,
82 #[label("expected: {exp_input_type}")]
83 dst_span: Span,
84 #[label("value originates here")]
85 src_span: Span,
86 },
87
88 #[error("Input type not supported.")]
98 #[diagnostic(code(nu::shell::only_supports_this_input_type))]
99 OnlySupportsThisInputType {
100 exp_input_type: String,
101 wrong_type: String,
102 #[label("only {exp_input_type} input data is supported")]
103 dst_span: Span,
104 #[label("input type: {wrong_type}")]
105 src_span: Span,
106 },
107
108 #[error("Pipeline empty.")]
114 #[diagnostic(code(nu::shell::pipeline_mismatch))]
115 PipelineEmpty {
116 #[label("no input value was piped in")]
117 dst_span: Span,
118 },
119
120 #[error("Type mismatch.")]
127 #[diagnostic(code(nu::shell::type_mismatch))]
128 TypeMismatch {
129 err_message: String,
130 #[label = "{err_message}"]
131 span: Span,
132 },
133
134 #[error("Type mismatch")]
140 #[diagnostic(code(nu::shell::type_mismatch))]
141 RuntimeTypeMismatch {
142 expected: Type,
143 actual: Type,
144 #[label = "expected {expected}, but got {actual}"]
145 span: Span,
146 },
147
148 #[error("Invalid value")]
154 #[diagnostic(code(nu::shell::invalid_value))]
155 InvalidValue {
156 valid: String,
157 actual: String,
158 #[label = "expected {valid}, but got {actual}"]
159 span: Span,
160 },
161
162 #[error("Incorrect value.")]
168 #[diagnostic(code(nu::shell::incorrect_value))]
169 IncorrectValue {
170 msg: String,
171 #[label = "{msg}"]
172 val_span: Span,
173 #[label = "encountered here"]
174 call_span: Span,
175 },
176
177 #[error("Assignment operations require a variable.")]
183 #[diagnostic(code(nu::shell::assignment_requires_variable))]
184 AssignmentRequiresVar {
185 #[label = "needs to be a variable"]
186 lhs_span: Span,
187 },
188
189 #[error("Assignment to an immutable variable.")]
195 #[diagnostic(code(nu::shell::assignment_requires_mutable_variable))]
196 AssignmentRequiresMutableVar {
197 #[label = "needs to be a mutable variable"]
198 lhs_span: Span,
199 },
200
201 #[error("Unknown operator: {op_token}.")]
207 #[diagnostic(code(nu::shell::unknown_operator))]
208 UnknownOperator {
209 op_token: String,
210 #[label = "unknown operator"]
211 span: Span,
212 },
213
214 #[error("Missing parameter: {param_name}.")]
220 #[diagnostic(code(nu::shell::missing_parameter))]
221 MissingParameter {
222 param_name: String,
223 #[label = "missing parameter: {param_name}"]
224 span: Span,
225 },
226
227 #[error("Incompatible parameters.")]
233 #[diagnostic(code(nu::shell::incompatible_parameters))]
234 IncompatibleParameters {
235 left_message: String,
236 #[label("{left_message}")]
238 left_span: Span,
239 right_message: String,
240 #[label("{right_message}")]
241 right_span: Span,
242 },
243
244 #[error("Delimiter error")]
250 #[diagnostic(code(nu::shell::delimiter_error))]
251 DelimiterError {
252 msg: String,
253 #[label("{msg}")]
254 span: Span,
255 },
256
257 #[error("Incompatible parameters.")]
265 #[diagnostic(code(nu::shell::incompatible_parameters))]
266 IncompatibleParametersSingle {
267 msg: String,
268 #[label = "{msg}"]
269 span: Span,
270 },
271
272 #[error("Running external commands not supported")]
278 #[diagnostic(code(nu::shell::external_commands))]
279 ExternalNotSupported {
280 #[label = "external not supported"]
281 span: Span,
282 },
283
284 #[error("Invalid Probability.")]
291 #[diagnostic(code(nu::shell::invalid_probability))]
292 InvalidProbability {
293 #[label = "invalid probability: must be between 0 and 1"]
294 span: Span,
295 },
296
297 #[error("Invalid range {left_flank}..{right_flank}")]
303 #[diagnostic(code(nu::shell::invalid_range))]
304 InvalidRange {
305 left_flank: String,
306 right_flank: String,
307 #[label = "expected a valid range"]
308 span: Span,
309 },
310
311 #[error("Nushell failed: {msg}.")]
317 #[diagnostic(
318 code(nu::shell::nushell_failed),
319 help(
320 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
321 )
322 )]
323 NushellFailed { msg: String },
325
326 #[error("Nushell failed: {msg}.")]
332 #[diagnostic(
333 code(nu::shell::nushell_failed_spanned),
334 help(
335 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
336 )
337 )]
338 NushellFailedSpanned {
340 msg: String,
341 label: String,
342 #[label = "{label}"]
343 span: Span,
344 },
345
346 #[error("Nushell failed: {msg}.")]
352 #[diagnostic(code(nu::shell::nushell_failed_help))]
353 NushellFailedHelp {
355 msg: String,
356 #[help]
357 help: String,
358 },
359
360 #[error("Variable not found")]
366 #[diagnostic(code(nu::shell::variable_not_found))]
367 VariableNotFoundAtRuntime {
368 #[label = "variable not found"]
369 span: Span,
370 },
371
372 #[error("Environment variable '{envvar_name}' not found")]
378 #[diagnostic(code(nu::shell::env_variable_not_found))]
379 EnvVarNotFoundAtRuntime {
380 envvar_name: String,
381 #[label = "environment variable not found"]
382 span: Span,
383 },
384
385 #[error("Module '{mod_name}' not found")]
391 #[diagnostic(code(nu::shell::module_not_found))]
392 ModuleNotFoundAtRuntime {
393 mod_name: String,
394 #[label = "module not found"]
395 span: Span,
396 },
397
398 #[error("Overlay '{overlay_name}' not found")]
404 #[diagnostic(code(nu::shell::overlay_not_found))]
405 OverlayNotFoundAtRuntime {
406 overlay_name: String,
407 #[label = "overlay not found"]
408 span: Span,
409 },
410
411 #[error("Not found.")]
417 #[diagnostic(code(nu::parser::not_found))]
418 NotFound {
419 #[label = "did not find anything under this name"]
420 span: Span,
421 },
422
423 #[error("Can't convert to {to_type}.")]
429 #[diagnostic(code(nu::shell::cant_convert))]
430 CantConvert {
431 to_type: String,
432 from_type: String,
433 #[label("can't convert {from_type} to {to_type}")]
434 span: Span,
435 #[help]
436 help: Option<String>,
437 },
438
439 #[error("Can't convert {from_type} to the specified unit.")]
445 #[diagnostic(code(nu::shell::cant_convert_value_to_unit))]
446 CantConvertToUnit {
447 to_type: String,
448 from_type: String,
449 #[label("can't convert {from_type} to {to_type}")]
450 span: Span,
451 #[label("conversion originates here")]
452 unit_span: Span,
453 #[help]
454 help: Option<String>,
455 },
456
457 #[error("'{envvar_name}' is not representable as a string.")]
463 #[diagnostic(
464 code(nu::shell::env_var_not_a_string),
465 help(
466 r#"The '{envvar_name}' environment variable must be a string or be convertible to a string.
467 Either make sure '{envvar_name}' is a string, or add a 'to_string' entry for it in ENV_CONVERSIONS."#
468 )
469 )]
470 EnvVarNotAString {
471 envvar_name: String,
472 #[label("value not representable as a string")]
473 span: Span,
474 },
475
476 #[error("{envvar_name} cannot be set manually.")]
482 #[diagnostic(
483 code(nu::shell::automatic_env_var_set_manually),
484 help(
485 r#"The environment variable '{envvar_name}' is set automatically by Nushell and cannot be set manually."#
486 )
487 )]
488 AutomaticEnvVarSetManually {
489 envvar_name: String,
490 #[label("cannot set '{envvar_name}' manually")]
491 span: Span,
492 },
493
494 #[error("Cannot replace environment.")]
501 #[diagnostic(
502 code(nu::shell::cannot_replace_env),
503 help(r#"Assigning a value to '$env' is not allowed."#)
504 )]
505 CannotReplaceEnv {
506 #[label("setting '$env' not allowed")]
507 span: Span,
508 },
509
510 #[error("Division by zero.")]
516 #[diagnostic(code(nu::shell::division_by_zero))]
517 DivisionByZero {
518 #[label("division by zero")]
519 span: Span,
520 },
521
522 #[error("Can't convert range to countable values")]
530 #[diagnostic(code(nu::shell::range_to_countable))]
531 CannotCreateRange {
532 #[label = "can't convert to countable values"]
533 span: Span,
534 },
535
536 #[error("Row number too large (max: {max_idx}).")]
542 #[diagnostic(code(nu::shell::access_beyond_end))]
543 AccessBeyondEnd {
544 max_idx: usize,
545 #[label = "index too large (max: {max_idx})"]
546 span: Span,
547 },
548
549 #[error("Inserted at wrong row number (should be {available_idx}).")]
555 #[diagnostic(code(nu::shell::access_beyond_end))]
556 InsertAfterNextFreeIndex {
557 available_idx: usize,
558 #[label = "can't insert at index (the next available index is {available_idx})"]
559 span: Span,
560 },
561
562 #[error("Row number too large (empty content).")]
568 #[diagnostic(code(nu::shell::access_beyond_end))]
569 AccessEmptyContent {
570 #[label = "index too large (empty content)"]
571 span: Span,
572 },
573
574 #[error("Row number too large.")]
581 #[diagnostic(code(nu::shell::access_beyond_end_of_stream))]
582 AccessBeyondEndOfStream {
583 #[label = "index too large"]
584 span: Span,
585 },
586
587 #[error("Data cannot be accessed with a cell path")]
593 #[diagnostic(code(nu::shell::incompatible_path_access))]
594 IncompatiblePathAccess {
595 type_name: String,
596 #[label("{type_name} doesn't support cell paths")]
597 span: Span,
598 },
599
600 #[error("Cannot find column '{col_name}'")]
606 #[diagnostic(code(nu::shell::column_not_found))]
607 CantFindColumn {
608 col_name: String,
609 #[label = "cannot find column '{col_name}'"]
610 span: Option<Span>,
611 #[label = "value originates here"]
612 src_span: Span,
613 },
614
615 #[error("Column already exists")]
621 #[diagnostic(code(nu::shell::column_already_exists))]
622 ColumnAlreadyExists {
623 col_name: String,
624 #[label = "column '{col_name}' already exists"]
625 span: Span,
626 #[label = "value originates here"]
627 src_span: Span,
628 },
629
630 #[error("Not a list value")]
636 #[diagnostic(code(nu::shell::not_a_list))]
637 NotAList {
638 #[label = "value not a list"]
639 dst_span: Span,
640 #[label = "value originates here"]
641 src_span: Span,
642 },
643
644 #[error("Record field or table column used twice: {col_name}")]
650 #[diagnostic(code(nu::shell::column_defined_twice))]
651 ColumnDefinedTwice {
652 col_name: String,
653 #[label = "field redefined here"]
654 second_use: Span,
655 #[label = "field first defined here"]
656 first_use: Span,
657 },
658
659 #[error("Attempted to create a record from different number of columns and values")]
665 #[diagnostic(code(nu::shell::record_cols_vals_mismatch))]
666 RecordColsValsMismatch {
667 #[label = "problematic value"]
668 bad_value: Span,
669 #[label = "attempted to create the record here"]
670 creation_site: Span,
671 },
672
673 #[error("Failed to detect columns")]
679 #[diagnostic(code(nu::shell::failed_to_detect_columns))]
680 ColumnDetectionFailure {
681 #[label = "value coming from here"]
682 bad_value: Span,
683 #[label = "tried to detect columns here"]
684 failure_site: Span,
685 },
686
687 #[error("Relative range values cannot be used with streams that don't have a known length")]
693 #[diagnostic(code(nu::shell::relative_range_on_infinite_stream))]
694 RelativeRangeOnInfiniteStream {
695 #[label = "Relative range values cannot be used with streams that don't have a known length"]
696 span: Span,
697 },
698
699 #[error("External command failed")]
705 #[diagnostic(code(nu::shell::external_command), help("{help}"))]
706 ExternalCommand {
707 label: String,
708 help: String,
709 #[label("{label}")]
710 span: Span,
711 },
712
713 #[error("External command had a non-zero exit code")]
719 #[diagnostic(code(nu::shell::non_zero_exit_code))]
720 NonZeroExitCode {
721 exit_code: NonZeroI32,
722 #[label("exited with code {exit_code}")]
723 span: Span,
724 },
725
726 #[cfg(unix)]
727 #[error("External command was terminated by a signal")]
733 #[diagnostic(code(nu::shell::terminated_by_signal))]
734 TerminatedBySignal {
735 signal_name: String,
736 signal: i32,
737 #[label("terminated by {signal_name} ({signal})")]
738 span: Span,
739 },
740
741 #[cfg(unix)]
742 #[error("External command core dumped")]
748 #[diagnostic(code(nu::shell::core_dumped))]
749 CoreDumped {
750 signal_name: String,
751 signal: i32,
752 #[label("core dumped with {signal_name} ({signal})")]
753 span: Span,
754 },
755
756 #[error("Unsupported input")]
762 #[diagnostic(code(nu::shell::unsupported_input))]
763 UnsupportedInput {
764 msg: String,
765 input: String,
766 #[label("{msg}")]
767 msg_span: Span,
768 #[label("{input}")]
769 input_span: Span,
770 },
771
772 #[error("Unable to parse datetime: [{msg}].")]
787 #[diagnostic(
788 code(nu::shell::datetime_parse_error),
789 help(
790 r#"Examples of supported inputs:
791 * "5 pm"
792 * "2020/12/4"
793 * "2020.12.04 22:10 +2"
794 * "2020-04-12 22:10:57 +02:00"
795 * "2020-04-12T22:10:57.213231+02:00"
796 * "Tue, 1 Jul 2003 10:52:37 +0200""#
797 )
798 )]
799 DatetimeParseError {
800 msg: String,
801 #[label("datetime parsing failed")]
802 span: Span,
803 },
804
805 #[error("Network failure")]
811 #[diagnostic(code(nu::shell::network_failure))]
812 NetworkFailure {
813 msg: String,
814 #[label("{msg}")]
815 span: Span,
816 },
817
818 #[error(transparent)]
819 #[diagnostic(transparent)]
820 Network(#[from] network::NetworkError),
821
822 #[error("Command not found")]
828 #[diagnostic(code(nu::shell::command_not_found))]
829 CommandNotFound {
830 #[label("command not found")]
831 span: Span,
832 },
833
834 #[error("Alias not found")]
840 #[diagnostic(code(nu::shell::alias_not_found))]
841 AliasNotFound {
842 #[label("alias not found")]
843 span: Span,
844 },
845
846 #[error("The registered plugin data for `{plugin_name}` is invalid")]
852 #[diagnostic(code(nu::shell::plugin_registry_data_invalid))]
853 PluginRegistryDataInvalid {
854 plugin_name: String,
855 #[label("plugin `{plugin_name}` loaded here")]
856 span: Option<Span>,
857 #[help(
858 "the format in the plugin registry file is not compatible with this version of Nushell.\n\nTry adding the plugin again with `{}`"
859 )]
860 add_command: String,
861 },
862
863 #[error("Plugin failed to load: {msg}")]
869 #[diagnostic(code(nu::shell::plugin_failed_to_load))]
870 PluginFailedToLoad { msg: String },
871
872 #[error("Plugin failed to encode: {msg}")]
878 #[diagnostic(code(nu::shell::plugin_failed_to_encode))]
879 PluginFailedToEncode { msg: String },
880
881 #[error("Plugin failed to decode: {msg}")]
887 #[diagnostic(code(nu::shell::plugin_failed_to_decode))]
888 PluginFailedToDecode { msg: String },
889
890 #[error("Custom value `{name}` cannot be sent to plugin")]
897 #[diagnostic(code(nu::shell::custom_value_incorrect_for_plugin))]
898 CustomValueIncorrectForPlugin {
899 name: String,
900 #[label("the `{dest_plugin}` plugin does not support this kind of value")]
901 span: Span,
902 dest_plugin: String,
903 #[help("this value came from the `{}` plugin")]
904 src_plugin: Option<String>,
905 },
906
907 #[error("Custom value failed to encode")]
914 #[diagnostic(code(nu::shell::custom_value_failed_to_encode))]
915 CustomValueFailedToEncode {
916 msg: String,
917 #[label("{msg}")]
918 span: Span,
919 },
920
921 #[error("Custom value failed to decode")]
928 #[diagnostic(code(nu::shell::custom_value_failed_to_decode))]
929 #[diagnostic(help("the plugin may have been updated and no longer support this custom value"))]
930 CustomValueFailedToDecode {
931 msg: String,
932 #[label("{msg}")]
933 span: Span,
934 },
935
936 #[error(transparent)]
942 #[diagnostic(transparent)]
943 Io(#[from] io::IoError),
944
945 #[error("Name not found")]
951 #[diagnostic(code(nu::shell::name_not_found))]
952 DidYouMean {
953 suggestion: String,
954 #[label("did you mean '{suggestion}'?")]
955 span: Span,
956 },
957
958 #[error("{msg}")]
964 #[diagnostic(code(nu::shell::did_you_mean_custom))]
965 DidYouMeanCustom {
966 msg: String,
967 suggestion: String,
968 #[label("did you mean '{suggestion}'?")]
969 span: Span,
970 },
971
972 #[error("Non-UTF8 string")]
978 #[diagnostic(
979 code(nu::parser::non_utf8),
980 help("see `decode` for handling character sets other than UTF-8")
981 )]
982 NonUtf8 {
983 #[label("non-UTF8 string")]
984 span: Span,
985 },
986
987 #[error("Non-UTF8 string")]
993 #[diagnostic(
994 code(nu::parser::non_utf8_custom),
995 help("see `decode` for handling character sets other than UTF-8")
996 )]
997 NonUtf8Custom {
998 msg: String,
999 #[label("{msg}")]
1000 span: Span,
1001 },
1002
1003 #[error("Encountered {} error(s) when updating config", errors.len())]
1009 #[diagnostic(code(nu::shell::invalid_config))]
1010 InvalidConfig {
1011 #[related]
1012 errors: Vec<ConfigError>,
1013 },
1014
1015 #[error("Value is missing a required '{column}' column")]
1021 #[diagnostic(code(nu::shell::missing_required_column))]
1022 MissingRequiredColumn {
1023 column: &'static str,
1024 #[label("has no '{column}' column")]
1025 span: Span,
1026 },
1027
1028 #[error("Negative value passed when positive one is required")]
1034 #[diagnostic(code(nu::shell::needs_positive_value))]
1035 NeedsPositiveValue {
1036 #[label("use a positive value")]
1037 span: Span,
1038 },
1039
1040 #[error("{error}")]
1042 #[diagnostic(code(nu::shell::error))]
1043 GenericError {
1044 error: String,
1045 msg: String,
1046 #[label("{msg}")]
1047 span: Option<Span>,
1048 #[help]
1049 help: Option<String>,
1050 #[related]
1051 inner: Vec<ShellError>,
1052 },
1053
1054 #[error("{error}")]
1056 #[diagnostic(code(nu::shell::outsidespan))]
1057 OutsideSpannedLabeledError {
1058 #[source_code]
1059 src: String,
1060 error: String,
1061 msg: String,
1062 #[label("{msg}")]
1063 span: Span,
1064 },
1065
1066 #[error("{msg}")]
1069 #[diagnostic(code(nu::shell::outside), url("{url}"))]
1070 OutsideSource {
1071 #[source_code]
1072 src: NamedSource<String>,
1073 msg: String,
1074 url: String,
1075 #[help]
1076 help: Option<String>,
1077 #[label(collection, "")]
1079 labels: Vec<LabeledSpan>,
1080 #[related]
1081 inner: Vec<ShellError>,
1082 },
1083
1084 #[error("{msg}")]
1087 #[diagnostic(code(nu::shell::outside))]
1088 OutsideSourceNoUrl {
1089 #[source_code]
1090 src: NamedSource<String>,
1091 msg: String,
1092 #[help]
1093 help: Option<String>,
1094 #[label(collection, "")]
1096 labels: Vec<LabeledSpan>,
1097 #[related]
1098 inner: Vec<ShellError>,
1099 },
1100
1101 #[error(transparent)]
1103 #[diagnostic(transparent)]
1104 LabeledError(#[from] Box<super::LabeledError>),
1105
1106 #[error("Removed command: {removed}")]
1112 #[diagnostic(code(nu::shell::removed_command))]
1113 RemovedCommand {
1114 removed: String,
1115 replacement: String,
1116 #[label("'{removed}' has been removed from Nushell. Please use '{replacement}' instead.")]
1117 span: Span,
1118 },
1119
1120 #[error("Eval block failed with pipeline input")]
1123 #[diagnostic(code(nu::shell::eval_block_with_input))]
1124 EvalBlockWithInput {
1125 #[label("source value")]
1126 span: Span,
1127 #[related]
1128 sources: Vec<ShellError>,
1129 },
1130
1131 #[error("Break used outside of loop")]
1133 Break {
1134 #[label("used outside of loop")]
1135 span: Span,
1136 },
1137
1138 #[error("Continue used outside of loop")]
1140 Continue {
1141 #[label("used outside of loop")]
1142 span: Span,
1143 },
1144
1145 #[error("Return used outside of custom command or closure")]
1147 Return {
1148 #[label("used outside of custom command or closure")]
1149 span: Span,
1150 value: Box<Value>,
1151 },
1152
1153 #[error("Exit doesn't catch internally")]
1155 #[diagnostic(
1156 code(nu::shell::exit),
1157 help(
1158 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
1159 )
1160 )]
1161 Exit { code: i32 },
1162
1163 #[error("Recursion limit ({recursion_limit}) reached")]
1169 #[diagnostic(code(nu::shell::recursion_limit_reached))]
1170 RecursionLimitReached {
1171 recursion_limit: u64,
1172 #[label("This called itself too many times")]
1173 span: Option<Span>,
1174 },
1175
1176 #[error("Operation interrupted")]
1178 Interrupted {
1179 #[label("This operation was interrupted")]
1180 span: Span,
1181 },
1182
1183 #[error("Match guard not bool")]
1186 #[diagnostic(
1187 code(nu::shell::match_guard_not_bool),
1188 help("Match guards should evaluate to a boolean")
1189 )]
1190 MatchGuardNotBool {
1191 #[label("not a boolean expression")]
1192 span: Span,
1193 },
1194
1195 #[error("Missing const eval implementation")]
1200 #[diagnostic(
1201 code(nu::shell::missing_const_eval_implementation),
1202 help(
1203 "The command lacks an implementation for constant evaluation. \
1204This is an internal Nushell error, please file an issue https://github.com/nushell/nushell/issues."
1205 )
1206 )]
1207 MissingConstEvalImpl {
1208 #[label("command lacks constant implementation")]
1209 span: Span,
1210 },
1211
1212 #[error("Found parsing error in expression.")]
1220 #[diagnostic(
1221 code(nu::shell::parse_error_in_constant),
1222 help(
1223 "This expression is supposed to be evaluated into a constant, which means error-free."
1224 )
1225 )]
1226 ParseErrorInConstant {
1227 #[label("Parsing error detected in expression")]
1228 span: Span,
1229 },
1230
1231 #[error("Not a constant.")]
1237 #[diagnostic(
1238 code(nu::shell::not_a_constant),
1239 help(
1240 "Only a subset of expressions are allowed constants during parsing. Try using the 'const' command or typing the value literally."
1241 )
1242 )]
1243 NotAConstant {
1244 #[label("Value is not a parse-time constant")]
1245 span: Span,
1246 },
1247
1248 #[error("Not a const command.")]
1255 #[diagnostic(
1256 code(nu::shell::not_a_const_command),
1257 help("Only a subset of builtin commands can run at parse time.")
1258 )]
1259 NotAConstCommand {
1260 #[label("This command cannot run at parse time.")]
1261 span: Span,
1262 },
1263
1264 #[error("Help message not a constant.")]
1270 #[diagnostic(
1271 code(nu::shell::not_a_const_help),
1272 help("Help messages are currently not supported to be constants.")
1273 )]
1274 NotAConstHelp {
1275 #[label("This command cannot run at parse time.")]
1276 span: Span,
1277 },
1278
1279 #[error("{deprecation_type} deprecated.")]
1280 #[diagnostic(code(nu::shell::deprecated), severity(Warning))]
1281 DeprecationWarning {
1282 deprecation_type: &'static str,
1283 suggestion: String,
1284 #[label("{suggestion}")]
1285 span: Span,
1286 #[help]
1287 help: Option<&'static str>,
1288 },
1289
1290 #[error("Invalid glob pattern")]
1296 #[diagnostic(
1297 code(nu::shell::invalid_glob_pattern),
1298 help("Refer to xxx for help on nushell glob patterns.")
1299 )]
1300 InvalidGlobPattern {
1301 msg: String,
1302 #[label("{msg}")]
1303 span: Span,
1304 },
1305
1306 #[error("Invalid unit")]
1312 #[diagnostic(
1313 code(nu::shell::invalid_unit),
1314 help("Supported units are: {supported_units}")
1315 )]
1316 InvalidUnit {
1317 supported_units: String,
1318 #[label("encountered here")]
1319 span: Span,
1320 },
1321
1322 #[error("Not a list")]
1328 #[diagnostic(
1329 code(nu::shell::cannot_spread_as_list),
1330 help(
1331 "Only lists can be spread inside lists and command calls. Try converting the value to a list before spreading."
1332 )
1333 )]
1334 CannotSpreadAsList {
1335 #[label = "cannot spread value"]
1336 span: Span,
1337 },
1338
1339 #[error("Not a record")]
1345 #[diagnostic(
1346 code(nu::shell::cannot_spread_as_record),
1347 help(
1348 "Only records can be spread inside records. Try converting the value to a record before spreading."
1349 )
1350 )]
1351 CannotSpreadAsRecord {
1352 #[label = "cannot spread value"]
1353 span: Span,
1354 },
1355
1356 #[error("Lists are not automatically spread when calling external commands")]
1362 #[diagnostic(
1363 code(nu::shell::cannot_pass_list_to_external),
1364 help("Either convert the list to a string or use the spread operator, like so: ...{arg}")
1365 )]
1366 CannotPassListToExternal {
1367 arg: String,
1368 #[label = "Spread operator (...) is necessary to spread lists"]
1369 span: Span,
1370 },
1371
1372 #[error(
1378 "The selected range {left_flank}..{right_flank} is out of the bounds of the provided input"
1379 )]
1380 #[diagnostic(code(nu::shell::out_of_bounds))]
1381 OutOfBounds {
1382 left_flank: String,
1383 right_flank: String,
1384 #[label = "byte index is not a char boundary or is out of bounds of the input"]
1385 span: Span,
1386 },
1387
1388 #[error("The config directory could not be found")]
1390 #[diagnostic(
1391 code(nu::shell::config_dir_not_found),
1392 help(
1393 r#"On Linux, this would be $XDG_CONFIG_HOME or $HOME/.config.
1394On MacOS, this would be `$HOME/Library/Application Support`.
1395On Windows, this would be %USERPROFILE%\AppData\Roaming"#
1396 )
1397 )]
1398 ConfigDirNotFound {
1399 #[label = "Could not find config directory"]
1400 span: Span,
1401 },
1402
1403 #[error(
1405 "$env.XDG_CONFIG_HOME ({xdg}) is invalid, using default config directory instead: {default}"
1406 )]
1407 #[diagnostic(
1408 code(nu::shell::xdg_config_home_invalid),
1409 help("Set XDG_CONFIG_HOME to an absolute path, or set it to an empty string to ignore it")
1410 )]
1411 InvalidXdgConfig { xdg: String, default: String },
1412
1413 #[error("IR evaluation error: {msg}")]
1420 #[diagnostic(
1421 code(nu::shell::ir_eval_error),
1422 help(
1423 "this is a bug, please report it at https://github.com/nushell/nushell/issues/new along with the code you were running if able"
1424 )
1425 )]
1426 IrEvalError {
1427 msg: String,
1428 #[label = "while running this code"]
1429 span: Option<Span>,
1430 },
1431
1432 #[error("OS feature is disabled: {msg}")]
1433 #[diagnostic(
1434 code(nu::shell::os_disabled),
1435 help("You're probably running outside an OS like a browser, we cannot support this")
1436 )]
1437 DisabledOsSupport {
1438 msg: String,
1439 #[label = "while running this code"]
1440 span: Span,
1441 },
1442
1443 #[error(transparent)]
1444 #[diagnostic(transparent)]
1445 Job(#[from] JobError),
1446
1447 #[error(transparent)]
1448 #[diagnostic(transparent)]
1449 ChainedError(ChainedError),
1450}
1451
1452impl ShellError {
1453 pub fn external_exit_code(&self) -> Option<Spanned<i32>> {
1454 let (item, span) = match *self {
1455 Self::NonZeroExitCode { exit_code, span } => (exit_code.into(), span),
1456 #[cfg(unix)]
1457 Self::TerminatedBySignal { signal, span, .. }
1458 | Self::CoreDumped { signal, span, .. } => (-signal, span),
1459 _ => return None,
1460 };
1461 Some(Spanned { item, span })
1462 }
1463
1464 pub fn exit_code(&self) -> Option<i32> {
1465 match self {
1466 Self::Return { .. } | Self::Break { .. } | Self::Continue { .. } => None,
1467 _ => self.external_exit_code().map(|e| e.item).or(Some(1)),
1468 }
1469 }
1470
1471 pub fn into_full_value(
1472 self,
1473 working_set: &StateWorkingSet,
1474 stack: &Stack,
1475 span: Span,
1476 ) -> Value {
1477 let exit_code = self.external_exit_code();
1478
1479 let mut record = record! {
1480 "msg" => Value::string(self.to_string(), span),
1481 "debug" => Value::string(format!("{self:?}"), span),
1482 "raw" => Value::error(self.clone(), span),
1483 "rendered" => Value::string(format_cli_error(Some(stack), working_set, &self, Some("nu::shell::error")), span),
1484 "json" => Value::string(serde_json::to_string(&self).expect("Could not serialize error"), span),
1485 };
1486
1487 if let Some(code) = exit_code {
1488 record.push("exit_code", Value::int(code.item.into(), code.span));
1489 }
1490
1491 Value::record(record, span)
1492 }
1493
1494 pub fn wrap(self, working_set: &StateWorkingSet, span: Span) -> ParseError {
1496 let msg = format_cli_error(None, working_set, &self, None);
1497 ParseError::LabeledError(
1498 msg,
1499 "Encountered error during parse-time evaluation".into(),
1500 span,
1501 )
1502 }
1503
1504 pub fn into_chained(self, span: Span) -> Self {
1506 Self::ChainedError(match self {
1507 Self::ChainedError(inner) => ChainedError::new_chained(inner, span),
1508 other => {
1509 let error = other.clone();
1512 let mut now = ChainedError::new(other, span);
1513 if let Some(related) = error.related() {
1514 let mapped = related
1515 .map(|s| {
1516 let shellerror: Self = Self::from_diagnostic(s);
1517 shellerror
1518 })
1519 .collect::<Vec<_>>();
1520 if !mapped.is_empty() {
1521 now.sources = [now.sources, mapped].concat();
1522 };
1523 }
1524 now
1525 }
1526 })
1527 }
1528
1529 pub fn from_diagnostic(diag: &(impl miette::Diagnostic + ?Sized)) -> Self {
1530 Self::LabeledError(LabeledError::from_diagnostic(diag).into())
1531 }
1532}
1533
1534impl FromValue for ShellError {
1535 fn from_value(v: Value) -> Result<Self, ShellError> {
1536 let from_type = v.get_type();
1537 match v {
1538 Value::Error { error, .. } => Ok(*error),
1539 Value::Record {
1541 val, internal_span, ..
1542 } => Self::from_value(
1543 (*val)
1544 .get("raw")
1545 .ok_or(ShellError::CantConvert {
1546 to_type: Self::expected_type().to_string(),
1547 from_type: from_type.to_string(),
1548 span: internal_span,
1549 help: None,
1550 })?
1551 .clone(),
1552 ),
1553 Value::Nothing { internal_span } => Ok(Self::GenericError {
1554 error: "error".into(),
1555 msg: "is nothing".into(),
1556 span: Some(internal_span),
1557 help: None,
1558 inner: vec![],
1559 }),
1560 _ => Err(ShellError::CantConvert {
1561 to_type: Self::expected_type().to_string(),
1562 from_type: v.get_type().to_string(),
1563 span: v.span(),
1564 help: None,
1565 }),
1566 }
1567 }
1568}
1569
1570impl From<Box<dyn std::error::Error>> for ShellError {
1571 fn from(error: Box<dyn std::error::Error>) -> ShellError {
1572 ShellError::GenericError {
1573 error: format!("{error:?}"),
1574 msg: error.to_string(),
1575 span: None,
1576 help: None,
1577 inner: vec![],
1578 }
1579 }
1580}
1581
1582impl From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
1583 fn from(error: Box<dyn std::error::Error + Send + Sync>) -> ShellError {
1584 ShellError::GenericError {
1585 error: format!("{error:?}"),
1586 msg: error.to_string(),
1587 span: None,
1588 help: None,
1589 inner: vec![],
1590 }
1591 }
1592}
1593
1594impl From<super::LabeledError> for ShellError {
1595 fn from(error: super::LabeledError) -> Self {
1596 ShellError::LabeledError(Box::new(error))
1597 }
1598}
1599
1600impl Serialize for ShellError {
1602 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1603 where
1604 S: serde::Serializer,
1605 {
1606 LabeledError::from_diagnostic(self).serialize(serializer)
1607 }
1608}
1609
1610impl<'de> Deserialize<'de> for ShellError {
1613 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1614 where
1615 D: serde::Deserializer<'de>,
1616 {
1617 LabeledError::deserialize(deserializer).map(ShellError::from)
1618 }
1619}
1620
1621#[test]
1622fn shell_error_serialize_roundtrip() {
1623 let original_error = ShellError::CantConvert {
1626 span: Span::new(100, 200),
1627 to_type: "Foo".into(),
1628 from_type: "Bar".into(),
1629 help: Some("this is a test".into()),
1630 };
1631 println!("orig_error = {original_error:#?}");
1632
1633 let serialized =
1634 serde_json::to_string_pretty(&original_error).expect("serde_json::to_string_pretty failed");
1635 println!("serialized = {serialized}");
1636
1637 let deserialized: ShellError =
1638 serde_json::from_str(&serialized).expect("serde_json::from_str failed");
1639 println!("deserialized = {deserialized:#?}");
1640
1641 assert_eq!(original_error.to_string(), deserialized.to_string());
1644
1645 assert_eq!(
1646 original_error.code().map(|c| c.to_string()),
1647 deserialized.code().map(|c| c.to_string())
1648 );
1649
1650 let orig_labels = original_error
1651 .labels()
1652 .into_iter()
1653 .flatten()
1654 .collect::<Vec<_>>();
1655 let deser_labels = deserialized
1656 .labels()
1657 .into_iter()
1658 .flatten()
1659 .collect::<Vec<_>>();
1660
1661 assert_eq!(orig_labels, deser_labels);
1662
1663 assert_eq!(
1664 original_error.help().map(|c| c.to_string()),
1665 deserialized.help().map(|c| c.to_string())
1666 );
1667}
1668
1669#[cfg(test)]
1670mod test {
1671 use super::*;
1672
1673 impl From<std::io::Error> for ShellError {
1674 fn from(_: std::io::Error) -> ShellError {
1675 unimplemented!(
1676 "This implementation is defined in the test module to ensure no other implementation exists."
1677 )
1678 }
1679 }
1680
1681 impl From<Spanned<std::io::Error>> for ShellError {
1682 fn from(_: Spanned<std::io::Error>) -> Self {
1683 unimplemented!(
1684 "This implementation is defined in the test module to ensure no other implementation exists."
1685 )
1686 }
1687 }
1688
1689 impl From<ShellError> for std::io::Error {
1690 fn from(_: ShellError) -> Self {
1691 unimplemented!(
1692 "This implementation is defined in the test module to ensure no other implementation exists."
1693 )
1694 }
1695 }
1696}