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("Recursion limit ({recursion_limit}) reached")]
1159 #[diagnostic(code(nu::shell::recursion_limit_reached))]
1160 RecursionLimitReached {
1161 recursion_limit: u64,
1162 #[label("This called itself too many times")]
1163 span: Option<Span>,
1164 },
1165
1166 #[error("Operation interrupted")]
1168 Interrupted {
1169 #[label("This operation was interrupted")]
1170 span: Span,
1171 },
1172
1173 #[error("Match guard not bool")]
1176 #[diagnostic(
1177 code(nu::shell::match_guard_not_bool),
1178 help("Match guards should evaluate to a boolean")
1179 )]
1180 MatchGuardNotBool {
1181 #[label("not a boolean expression")]
1182 span: Span,
1183 },
1184
1185 #[error("Missing const eval implementation")]
1190 #[diagnostic(
1191 code(nu::shell::missing_const_eval_implementation),
1192 help(
1193 "The command lacks an implementation for constant evaluation. \
1194This is an internal Nushell error, please file an issue https://github.com/nushell/nushell/issues."
1195 )
1196 )]
1197 MissingConstEvalImpl {
1198 #[label("command lacks constant implementation")]
1199 span: Span,
1200 },
1201
1202 #[error("Found parsing error in expression.")]
1210 #[diagnostic(
1211 code(nu::shell::parse_error_in_constant),
1212 help(
1213 "This expression is supposed to be evaluated into a constant, which means error-free."
1214 )
1215 )]
1216 ParseErrorInConstant {
1217 #[label("Parsing error detected in expression")]
1218 span: Span,
1219 },
1220
1221 #[error("Not a constant.")]
1227 #[diagnostic(
1228 code(nu::shell::not_a_constant),
1229 help(
1230 "Only a subset of expressions are allowed constants during parsing. Try using the 'const' command or typing the value literally."
1231 )
1232 )]
1233 NotAConstant {
1234 #[label("Value is not a parse-time constant")]
1235 span: Span,
1236 },
1237
1238 #[error("Not a const command.")]
1245 #[diagnostic(
1246 code(nu::shell::not_a_const_command),
1247 help("Only a subset of builtin commands can run at parse time.")
1248 )]
1249 NotAConstCommand {
1250 #[label("This command cannot run at parse time.")]
1251 span: Span,
1252 },
1253
1254 #[error("Help message not a constant.")]
1260 #[diagnostic(
1261 code(nu::shell::not_a_const_help),
1262 help("Help messages are currently not supported to be constants.")
1263 )]
1264 NotAConstHelp {
1265 #[label("This command cannot run at parse time.")]
1266 span: Span,
1267 },
1268
1269 #[error("{deprecation_type} deprecated.")]
1270 #[diagnostic(code(nu::shell::deprecated), severity(Warning))]
1271 DeprecationWarning {
1272 deprecation_type: &'static str,
1273 suggestion: String,
1274 #[label("{suggestion}")]
1275 span: Span,
1276 #[help]
1277 help: Option<&'static str>,
1278 },
1279
1280 #[error("Invalid glob pattern")]
1286 #[diagnostic(
1287 code(nu::shell::invalid_glob_pattern),
1288 help("Refer to xxx for help on nushell glob patterns.")
1289 )]
1290 InvalidGlobPattern {
1291 msg: String,
1292 #[label("{msg}")]
1293 span: Span,
1294 },
1295
1296 #[error("Invalid unit")]
1302 #[diagnostic(
1303 code(nu::shell::invalid_unit),
1304 help("Supported units are: {supported_units}")
1305 )]
1306 InvalidUnit {
1307 supported_units: String,
1308 #[label("encountered here")]
1309 span: Span,
1310 },
1311
1312 #[error("Not a list")]
1318 #[diagnostic(
1319 code(nu::shell::cannot_spread_as_list),
1320 help(
1321 "Only lists can be spread inside lists and command calls. Try converting the value to a list before spreading."
1322 )
1323 )]
1324 CannotSpreadAsList {
1325 #[label = "cannot spread value"]
1326 span: Span,
1327 },
1328
1329 #[error("Not a record")]
1335 #[diagnostic(
1336 code(nu::shell::cannot_spread_as_record),
1337 help(
1338 "Only records can be spread inside records. Try converting the value to a record before spreading."
1339 )
1340 )]
1341 CannotSpreadAsRecord {
1342 #[label = "cannot spread value"]
1343 span: Span,
1344 },
1345
1346 #[error("Lists are not automatically spread when calling external commands")]
1352 #[diagnostic(
1353 code(nu::shell::cannot_pass_list_to_external),
1354 help("Either convert the list to a string or use the spread operator, like so: ...{arg}")
1355 )]
1356 CannotPassListToExternal {
1357 arg: String,
1358 #[label = "Spread operator (...) is necessary to spread lists"]
1359 span: Span,
1360 },
1361
1362 #[error(
1368 "The selected range {left_flank}..{right_flank} is out of the bounds of the provided input"
1369 )]
1370 #[diagnostic(code(nu::shell::out_of_bounds))]
1371 OutOfBounds {
1372 left_flank: String,
1373 right_flank: String,
1374 #[label = "byte index is not a char boundary or is out of bounds of the input"]
1375 span: Span,
1376 },
1377
1378 #[error("The config directory could not be found")]
1380 #[diagnostic(
1381 code(nu::shell::config_dir_not_found),
1382 help(
1383 r#"On Linux, this would be $XDG_CONFIG_HOME or $HOME/.config.
1384On MacOS, this would be `$HOME/Library/Application Support`.
1385On Windows, this would be %USERPROFILE%\AppData\Roaming"#
1386 )
1387 )]
1388 ConfigDirNotFound {
1389 #[label = "Could not find config directory"]
1390 span: Span,
1391 },
1392
1393 #[error(
1395 "$env.XDG_CONFIG_HOME ({xdg}) is invalid, using default config directory instead: {default}"
1396 )]
1397 #[diagnostic(
1398 code(nu::shell::xdg_config_home_invalid),
1399 help("Set XDG_CONFIG_HOME to an absolute path, or set it to an empty string to ignore it")
1400 )]
1401 InvalidXdgConfig { xdg: String, default: String },
1402
1403 #[error("IR evaluation error: {msg}")]
1410 #[diagnostic(
1411 code(nu::shell::ir_eval_error),
1412 help(
1413 "this is a bug, please report it at https://github.com/nushell/nushell/issues/new along with the code you were running if able"
1414 )
1415 )]
1416 IrEvalError {
1417 msg: String,
1418 #[label = "while running this code"]
1419 span: Option<Span>,
1420 },
1421
1422 #[error("OS feature is disabled: {msg}")]
1423 #[diagnostic(
1424 code(nu::shell::os_disabled),
1425 help("You're probably running outside an OS like a browser, we cannot support this")
1426 )]
1427 DisabledOsSupport {
1428 msg: String,
1429 #[label = "while running this code"]
1430 span: Span,
1431 },
1432
1433 #[error(transparent)]
1434 #[diagnostic(transparent)]
1435 Job(#[from] JobError),
1436
1437 #[error(transparent)]
1438 #[diagnostic(transparent)]
1439 ChainedError(ChainedError),
1440}
1441
1442impl ShellError {
1443 pub fn external_exit_code(&self) -> Option<Spanned<i32>> {
1444 let (item, span) = match *self {
1445 Self::NonZeroExitCode { exit_code, span } => (exit_code.into(), span),
1446 #[cfg(unix)]
1447 Self::TerminatedBySignal { signal, span, .. }
1448 | Self::CoreDumped { signal, span, .. } => (-signal, span),
1449 _ => return None,
1450 };
1451 Some(Spanned { item, span })
1452 }
1453
1454 pub fn exit_code(&self) -> Option<i32> {
1455 match self {
1456 Self::Return { .. } | Self::Break { .. } | Self::Continue { .. } => None,
1457 _ => self.external_exit_code().map(|e| e.item).or(Some(1)),
1458 }
1459 }
1460
1461 pub fn into_full_value(
1462 self,
1463 working_set: &StateWorkingSet,
1464 stack: &Stack,
1465 span: Span,
1466 ) -> Value {
1467 let exit_code = self.external_exit_code();
1468
1469 let mut record = record! {
1470 "msg" => Value::string(self.to_string(), span),
1471 "debug" => Value::string(format!("{self:?}"), span),
1472 "raw" => Value::error(self.clone(), span),
1473 "rendered" => Value::string(format_cli_error(Some(stack), working_set, &self, Some("nu::shell::error")), span),
1474 "json" => Value::string(serde_json::to_string(&self).expect("Could not serialize error"), span),
1475 };
1476
1477 if let Some(code) = exit_code {
1478 record.push("exit_code", Value::int(code.item.into(), code.span));
1479 }
1480
1481 Value::record(record, span)
1482 }
1483
1484 pub fn wrap(self, working_set: &StateWorkingSet, span: Span) -> ParseError {
1486 let msg = format_cli_error(None, working_set, &self, None);
1487 ParseError::LabeledError(
1488 msg,
1489 "Encountered error during parse-time evaluation".into(),
1490 span,
1491 )
1492 }
1493
1494 pub fn into_chained(self, span: Span) -> Self {
1496 Self::ChainedError(match self {
1497 Self::ChainedError(inner) => ChainedError::new_chained(inner, span),
1498 other => {
1499 let error = other.clone();
1502 let mut now = ChainedError::new(other, span);
1503 if let Some(related) = error.related() {
1504 let mapped = related
1505 .map(|s| {
1506 let shellerror: Self = Self::from_diagnostic(s);
1507 shellerror
1508 })
1509 .collect::<Vec<_>>();
1510 if !mapped.is_empty() {
1511 now.sources = [now.sources, mapped].concat();
1512 };
1513 }
1514 now
1515 }
1516 })
1517 }
1518
1519 pub fn from_diagnostic(diag: &(impl miette::Diagnostic + ?Sized)) -> Self {
1520 Self::LabeledError(LabeledError::from_diagnostic(diag).into())
1521 }
1522}
1523
1524impl FromValue for ShellError {
1525 fn from_value(v: Value) -> Result<Self, ShellError> {
1526 let from_type = v.get_type();
1527 match v {
1528 Value::Error { error, .. } => Ok(*error),
1529 Value::Record {
1531 val, internal_span, ..
1532 } => Self::from_value(
1533 (*val)
1534 .get("raw")
1535 .ok_or(ShellError::CantConvert {
1536 to_type: Self::expected_type().to_string(),
1537 from_type: from_type.to_string(),
1538 span: internal_span,
1539 help: None,
1540 })?
1541 .clone(),
1542 ),
1543 Value::Nothing { internal_span } => Ok(Self::GenericError {
1544 error: "error".into(),
1545 msg: "is nothing".into(),
1546 span: Some(internal_span),
1547 help: None,
1548 inner: vec![],
1549 }),
1550 _ => Err(ShellError::CantConvert {
1551 to_type: Self::expected_type().to_string(),
1552 from_type: v.get_type().to_string(),
1553 span: v.span(),
1554 help: None,
1555 }),
1556 }
1557 }
1558}
1559
1560impl From<Box<dyn std::error::Error>> for ShellError {
1561 fn from(error: Box<dyn std::error::Error>) -> ShellError {
1562 ShellError::GenericError {
1563 error: format!("{error:?}"),
1564 msg: error.to_string(),
1565 span: None,
1566 help: None,
1567 inner: vec![],
1568 }
1569 }
1570}
1571
1572impl From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
1573 fn from(error: Box<dyn std::error::Error + Send + Sync>) -> ShellError {
1574 ShellError::GenericError {
1575 error: format!("{error:?}"),
1576 msg: error.to_string(),
1577 span: None,
1578 help: None,
1579 inner: vec![],
1580 }
1581 }
1582}
1583
1584impl From<super::LabeledError> for ShellError {
1585 fn from(error: super::LabeledError) -> Self {
1586 ShellError::LabeledError(Box::new(error))
1587 }
1588}
1589
1590impl Serialize for ShellError {
1592 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1593 where
1594 S: serde::Serializer,
1595 {
1596 LabeledError::from_diagnostic(self).serialize(serializer)
1597 }
1598}
1599
1600impl<'de> Deserialize<'de> for ShellError {
1603 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1604 where
1605 D: serde::Deserializer<'de>,
1606 {
1607 LabeledError::deserialize(deserializer).map(ShellError::from)
1608 }
1609}
1610
1611#[test]
1612fn shell_error_serialize_roundtrip() {
1613 let original_error = ShellError::CantConvert {
1616 span: Span::new(100, 200),
1617 to_type: "Foo".into(),
1618 from_type: "Bar".into(),
1619 help: Some("this is a test".into()),
1620 };
1621 println!("orig_error = {original_error:#?}");
1622
1623 let serialized =
1624 serde_json::to_string_pretty(&original_error).expect("serde_json::to_string_pretty failed");
1625 println!("serialized = {serialized}");
1626
1627 let deserialized: ShellError =
1628 serde_json::from_str(&serialized).expect("serde_json::from_str failed");
1629 println!("deserialized = {deserialized:#?}");
1630
1631 assert_eq!(original_error.to_string(), deserialized.to_string());
1634
1635 assert_eq!(
1636 original_error.code().map(|c| c.to_string()),
1637 deserialized.code().map(|c| c.to_string())
1638 );
1639
1640 let orig_labels = original_error
1641 .labels()
1642 .into_iter()
1643 .flatten()
1644 .collect::<Vec<_>>();
1645 let deser_labels = deserialized
1646 .labels()
1647 .into_iter()
1648 .flatten()
1649 .collect::<Vec<_>>();
1650
1651 assert_eq!(orig_labels, deser_labels);
1652
1653 assert_eq!(
1654 original_error.help().map(|c| c.to_string()),
1655 deserialized.help().map(|c| c.to_string())
1656 );
1657}
1658
1659#[cfg(test)]
1660mod test {
1661 use super::*;
1662
1663 impl From<std::io::Error> for ShellError {
1664 fn from(_: std::io::Error) -> ShellError {
1665 unimplemented!(
1666 "This implementation is defined in the test module to ensure no other implementation exists."
1667 )
1668 }
1669 }
1670
1671 impl From<Spanned<std::io::Error>> for ShellError {
1672 fn from(_: Spanned<std::io::Error>) -> Self {
1673 unimplemented!(
1674 "This implementation is defined in the test module to ensure no other implementation exists."
1675 )
1676 }
1677 }
1678
1679 impl From<ShellError> for std::io::Error {
1680 fn from(_: ShellError) -> Self {
1681 unimplemented!(
1682 "This implementation is defined in the test module to ensure no other implementation exists."
1683 )
1684 }
1685 }
1686}