1use super::chained_error::ChainedError;
2use crate::{
3 ConfigError, LabeledError, ParseError, Span, Spanned, Type, Value, ast::Operator,
4 engine::StateWorkingSet, format_cli_error, record,
5};
6use job::JobError;
7use miette::Diagnostic;
8use serde::{Deserialize, Serialize};
9use std::num::NonZeroI32;
10use thiserror::Error;
11
12pub mod bridge;
13pub mod io;
14pub mod job;
15pub mod location;
16
17#[derive(Debug, Clone, Error, Diagnostic, PartialEq)]
21pub enum ShellError {
22 #[error("The '{op}' operator does not work on values of type '{unsupported}'.")]
24 #[diagnostic(code(nu::shell::operator_unsupported_type))]
25 OperatorUnsupportedType {
26 op: Operator,
27 unsupported: Type,
28 #[label = "does not support '{unsupported}'"]
29 op_span: Span,
30 #[label("{unsupported}")]
31 unsupported_span: Span,
32 #[help]
33 help: Option<&'static str>,
34 },
35
36 #[error("Types '{lhs}' and '{rhs}' are not compatible for the '{op}' operator.")]
38 #[diagnostic(code(nu::shell::operator_incompatible_types))]
39 OperatorIncompatibleTypes {
40 op: Operator,
41 lhs: Type,
42 rhs: Type,
43 #[label = "does not operate between '{lhs}' and '{rhs}'"]
44 op_span: Span,
45 #[label("{lhs}")]
46 lhs_span: Span,
47 #[label("{rhs}")]
48 rhs_span: Span,
49 #[help]
50 help: Option<&'static str>,
51 },
52
53 #[error("Operator overflow.")]
60 #[diagnostic(code(nu::shell::operator_overflow))]
61 OperatorOverflow {
62 msg: String,
63 #[label = "{msg}"]
64 span: Span,
65 #[help]
66 help: Option<String>,
67 },
68
69 #[error("Pipeline mismatch.")]
76 #[diagnostic(code(nu::shell::pipeline_mismatch))]
77 PipelineMismatch {
78 exp_input_type: String,
79 #[label("expected: {exp_input_type}")]
80 dst_span: Span,
81 #[label("value originates here")]
82 src_span: Span,
83 },
84
85 #[error("Input type not supported.")]
95 #[diagnostic(code(nu::shell::only_supports_this_input_type))]
96 OnlySupportsThisInputType {
97 exp_input_type: String,
98 wrong_type: String,
99 #[label("only {exp_input_type} input data is supported")]
100 dst_span: Span,
101 #[label("input type: {wrong_type}")]
102 src_span: Span,
103 },
104
105 #[error("Pipeline empty.")]
111 #[diagnostic(code(nu::shell::pipeline_mismatch))]
112 PipelineEmpty {
113 #[label("no input value was piped in")]
114 dst_span: Span,
115 },
116
117 #[error("Type mismatch.")]
124 #[diagnostic(code(nu::shell::type_mismatch))]
125 TypeMismatch {
126 err_message: String,
127 #[label = "{err_message}"]
128 span: Span,
129 },
130
131 #[error("Type mismatch")]
137 #[diagnostic(code(nu::shell::type_mismatch))]
138 RuntimeTypeMismatch {
139 expected: Type,
140 actual: Type,
141 #[label = "expected {expected}, but got {actual}"]
142 span: Span,
143 },
144
145 #[error("Invalid value")]
151 #[diagnostic(code(nu::shell::invalid_value))]
152 InvalidValue {
153 valid: String,
154 actual: String,
155 #[label = "expected {valid}, but got {actual}"]
156 span: Span,
157 },
158
159 #[error("Incorrect value.")]
165 #[diagnostic(code(nu::shell::incorrect_value))]
166 IncorrectValue {
167 msg: String,
168 #[label = "{msg}"]
169 val_span: Span,
170 #[label = "encountered here"]
171 call_span: Span,
172 },
173
174 #[error("Assignment operations require a variable.")]
180 #[diagnostic(code(nu::shell::assignment_requires_variable))]
181 AssignmentRequiresVar {
182 #[label = "needs to be a variable"]
183 lhs_span: Span,
184 },
185
186 #[error("Assignment to an immutable variable.")]
192 #[diagnostic(code(nu::shell::assignment_requires_mutable_variable))]
193 AssignmentRequiresMutableVar {
194 #[label = "needs to be a mutable variable"]
195 lhs_span: Span,
196 },
197
198 #[error("Unknown operator: {op_token}.")]
204 #[diagnostic(code(nu::shell::unknown_operator))]
205 UnknownOperator {
206 op_token: String,
207 #[label = "unknown operator"]
208 span: Span,
209 },
210
211 #[error("Missing parameter: {param_name}.")]
217 #[diagnostic(code(nu::shell::missing_parameter))]
218 MissingParameter {
219 param_name: String,
220 #[label = "missing parameter: {param_name}"]
221 span: Span,
222 },
223
224 #[error("Incompatible parameters.")]
230 #[diagnostic(code(nu::shell::incompatible_parameters))]
231 IncompatibleParameters {
232 left_message: String,
233 #[label("{left_message}")]
235 left_span: Span,
236 right_message: String,
237 #[label("{right_message}")]
238 right_span: Span,
239 },
240
241 #[error("Delimiter error")]
247 #[diagnostic(code(nu::shell::delimiter_error))]
248 DelimiterError {
249 msg: String,
250 #[label("{msg}")]
251 span: Span,
252 },
253
254 #[error("Incompatible parameters.")]
262 #[diagnostic(code(nu::shell::incompatible_parameters))]
263 IncompatibleParametersSingle {
264 msg: String,
265 #[label = "{msg}"]
266 span: Span,
267 },
268
269 #[error("Running external commands not supported")]
275 #[diagnostic(code(nu::shell::external_commands))]
276 ExternalNotSupported {
277 #[label = "external not supported"]
278 span: Span,
279 },
280
281 #[error("Invalid Probability.")]
288 #[diagnostic(code(nu::shell::invalid_probability))]
289 InvalidProbability {
290 #[label = "invalid probability: must be between 0 and 1"]
291 span: Span,
292 },
293
294 #[error("Invalid range {left_flank}..{right_flank}")]
300 #[diagnostic(code(nu::shell::invalid_range))]
301 InvalidRange {
302 left_flank: String,
303 right_flank: String,
304 #[label = "expected a valid range"]
305 span: Span,
306 },
307
308 #[error("Nushell failed: {msg}.")]
314 #[diagnostic(
315 code(nu::shell::nushell_failed),
316 help(
317 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
318 )
319 )]
320 NushellFailed { msg: String },
322
323 #[error("Nushell failed: {msg}.")]
329 #[diagnostic(
330 code(nu::shell::nushell_failed_spanned),
331 help(
332 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
333 )
334 )]
335 NushellFailedSpanned {
337 msg: String,
338 label: String,
339 #[label = "{label}"]
340 span: Span,
341 },
342
343 #[error("Nushell failed: {msg}.")]
349 #[diagnostic(code(nu::shell::nushell_failed_help))]
350 NushellFailedHelp {
352 msg: String,
353 #[help]
354 help: String,
355 },
356
357 #[error("Variable not found")]
363 #[diagnostic(code(nu::shell::variable_not_found))]
364 VariableNotFoundAtRuntime {
365 #[label = "variable not found"]
366 span: Span,
367 },
368
369 #[error("Environment variable '{envvar_name}' not found")]
375 #[diagnostic(code(nu::shell::env_variable_not_found))]
376 EnvVarNotFoundAtRuntime {
377 envvar_name: String,
378 #[label = "environment variable not found"]
379 span: Span,
380 },
381
382 #[error("Module '{mod_name}' not found")]
388 #[diagnostic(code(nu::shell::module_not_found))]
389 ModuleNotFoundAtRuntime {
390 mod_name: String,
391 #[label = "module not found"]
392 span: Span,
393 },
394
395 #[error("Overlay '{overlay_name}' not found")]
401 #[diagnostic(code(nu::shell::overlay_not_found))]
402 OverlayNotFoundAtRuntime {
403 overlay_name: String,
404 #[label = "overlay not found"]
405 span: Span,
406 },
407
408 #[error("Not found.")]
414 #[diagnostic(code(nu::parser::not_found))]
415 NotFound {
416 #[label = "did not find anything under this name"]
417 span: Span,
418 },
419
420 #[error("Can't convert to {to_type}.")]
426 #[diagnostic(code(nu::shell::cant_convert))]
427 CantConvert {
428 to_type: String,
429 from_type: String,
430 #[label("can't convert {from_type} to {to_type}")]
431 span: Span,
432 #[help]
433 help: Option<String>,
434 },
435
436 #[error("Can't convert {from_type} to the specified unit.")]
442 #[diagnostic(code(nu::shell::cant_convert_value_to_unit))]
443 CantConvertToUnit {
444 to_type: String,
445 from_type: String,
446 #[label("can't convert {from_type} to {to_type}")]
447 span: Span,
448 #[label("conversion originates here")]
449 unit_span: Span,
450 #[help]
451 help: Option<String>,
452 },
453
454 #[error("'{envvar_name}' is not representable as a string.")]
460 #[diagnostic(
461 code(nu::shell::env_var_not_a_string),
462 help(
463 r#"The '{envvar_name}' environment variable must be a string or be convertible to a string.
464 Either make sure '{envvar_name}' is a string, or add a 'to_string' entry for it in ENV_CONVERSIONS."#
465 )
466 )]
467 EnvVarNotAString {
468 envvar_name: String,
469 #[label("value not representable as a string")]
470 span: Span,
471 },
472
473 #[error("{envvar_name} cannot be set manually.")]
479 #[diagnostic(
480 code(nu::shell::automatic_env_var_set_manually),
481 help(
482 r#"The environment variable '{envvar_name}' is set automatically by Nushell and cannot be set manually."#
483 )
484 )]
485 AutomaticEnvVarSetManually {
486 envvar_name: String,
487 #[label("cannot set '{envvar_name}' manually")]
488 span: Span,
489 },
490
491 #[error("Cannot replace environment.")]
498 #[diagnostic(
499 code(nu::shell::cannot_replace_env),
500 help(r#"Assigning a value to '$env' is not allowed."#)
501 )]
502 CannotReplaceEnv {
503 #[label("setting '$env' not allowed")]
504 span: Span,
505 },
506
507 #[error("Division by zero.")]
513 #[diagnostic(code(nu::shell::division_by_zero))]
514 DivisionByZero {
515 #[label("division by zero")]
516 span: Span,
517 },
518
519 #[error("Can't convert range to countable values")]
527 #[diagnostic(code(nu::shell::range_to_countable))]
528 CannotCreateRange {
529 #[label = "can't convert to countable values"]
530 span: Span,
531 },
532
533 #[error("Row number too large (max: {max_idx}).")]
539 #[diagnostic(code(nu::shell::access_beyond_end))]
540 AccessBeyondEnd {
541 max_idx: usize,
542 #[label = "index too large (max: {max_idx})"]
543 span: Span,
544 },
545
546 #[error("Inserted at wrong row number (should be {available_idx}).")]
552 #[diagnostic(code(nu::shell::access_beyond_end))]
553 InsertAfterNextFreeIndex {
554 available_idx: usize,
555 #[label = "can't insert at index (the next available index is {available_idx})"]
556 span: Span,
557 },
558
559 #[error("Row number too large (empty content).")]
565 #[diagnostic(code(nu::shell::access_beyond_end))]
566 AccessEmptyContent {
567 #[label = "index too large (empty content)"]
568 span: Span,
569 },
570
571 #[error("Row number too large.")]
578 #[diagnostic(code(nu::shell::access_beyond_end_of_stream))]
579 AccessBeyondEndOfStream {
580 #[label = "index too large"]
581 span: Span,
582 },
583
584 #[error("Data cannot be accessed with a cell path")]
590 #[diagnostic(code(nu::shell::incompatible_path_access))]
591 IncompatiblePathAccess {
592 type_name: String,
593 #[label("{type_name} doesn't support cell paths")]
594 span: Span,
595 },
596
597 #[error("Cannot find column '{col_name}'")]
603 #[diagnostic(code(nu::shell::column_not_found))]
604 CantFindColumn {
605 col_name: String,
606 #[label = "cannot find column '{col_name}'"]
607 span: Option<Span>,
608 #[label = "value originates here"]
609 src_span: Span,
610 },
611
612 #[error("Column already exists")]
618 #[diagnostic(code(nu::shell::column_already_exists))]
619 ColumnAlreadyExists {
620 col_name: String,
621 #[label = "column '{col_name}' already exists"]
622 span: Span,
623 #[label = "value originates here"]
624 src_span: Span,
625 },
626
627 #[error("Not a list value")]
633 #[diagnostic(code(nu::shell::not_a_list))]
634 NotAList {
635 #[label = "value not a list"]
636 dst_span: Span,
637 #[label = "value originates here"]
638 src_span: Span,
639 },
640
641 #[error("Record field or table column used twice: {col_name}")]
647 #[diagnostic(code(nu::shell::column_defined_twice))]
648 ColumnDefinedTwice {
649 col_name: String,
650 #[label = "field redefined here"]
651 second_use: Span,
652 #[label = "field first defined here"]
653 first_use: Span,
654 },
655
656 #[error("Attempted to create a record from different number of columns and values")]
662 #[diagnostic(code(nu::shell::record_cols_vals_mismatch))]
663 RecordColsValsMismatch {
664 #[label = "problematic value"]
665 bad_value: Span,
666 #[label = "attempted to create the record here"]
667 creation_site: Span,
668 },
669
670 #[error("Failed to detect columns")]
676 #[diagnostic(code(nu::shell::failed_to_detect_columns))]
677 ColumnDetectionFailure {
678 #[label = "value coming from here"]
679 bad_value: Span,
680 #[label = "tried to detect columns here"]
681 failure_site: Span,
682 },
683
684 #[error("Relative range values cannot be used with streams that don't have a known length")]
690 #[diagnostic(code(nu::shell::relative_range_on_infinite_stream))]
691 RelativeRangeOnInfiniteStream {
692 #[label = "Relative range values cannot be used with streams that don't have a known length"]
693 span: Span,
694 },
695
696 #[error("External command failed")]
702 #[diagnostic(code(nu::shell::external_command), help("{help}"))]
703 ExternalCommand {
704 label: String,
705 help: String,
706 #[label("{label}")]
707 span: Span,
708 },
709
710 #[error("External command had a non-zero exit code")]
716 #[diagnostic(code(nu::shell::non_zero_exit_code))]
717 NonZeroExitCode {
718 exit_code: NonZeroI32,
719 #[label("exited with code {exit_code}")]
720 span: Span,
721 },
722
723 #[cfg(unix)]
724 #[error("External command was terminated by a signal")]
730 #[diagnostic(code(nu::shell::terminated_by_signal))]
731 TerminatedBySignal {
732 signal_name: String,
733 signal: i32,
734 #[label("terminated by {signal_name} ({signal})")]
735 span: Span,
736 },
737
738 #[cfg(unix)]
739 #[error("External command core dumped")]
745 #[diagnostic(code(nu::shell::core_dumped))]
746 CoreDumped {
747 signal_name: String,
748 signal: i32,
749 #[label("core dumped with {signal_name} ({signal})")]
750 span: Span,
751 },
752
753 #[error("Unsupported input")]
759 #[diagnostic(code(nu::shell::unsupported_input))]
760 UnsupportedInput {
761 msg: String,
762 input: String,
763 #[label("{msg}")]
764 msg_span: Span,
765 #[label("{input}")]
766 input_span: Span,
767 },
768
769 #[error("Unable to parse datetime: [{msg}].")]
784 #[diagnostic(
785 code(nu::shell::datetime_parse_error),
786 help(
787 r#"Examples of supported inputs:
788 * "5 pm"
789 * "2020/12/4"
790 * "2020.12.04 22:10 +2"
791 * "2020-04-12 22:10:57 +02:00"
792 * "2020-04-12T22:10:57.213231+02:00"
793 * "Tue, 1 Jul 2003 10:52:37 +0200""#
794 )
795 )]
796 DatetimeParseError {
797 msg: String,
798 #[label("datetime parsing failed")]
799 span: Span,
800 },
801
802 #[error("Network failure")]
808 #[diagnostic(code(nu::shell::network_failure))]
809 NetworkFailure {
810 msg: String,
811 #[label("{msg}")]
812 span: Span,
813 },
814
815 #[error("Command not found")]
821 #[diagnostic(code(nu::shell::command_not_found))]
822 CommandNotFound {
823 #[label("command not found")]
824 span: Span,
825 },
826
827 #[error("Alias not found")]
833 #[diagnostic(code(nu::shell::alias_not_found))]
834 AliasNotFound {
835 #[label("alias not found")]
836 span: Span,
837 },
838
839 #[error("The registered plugin data for `{plugin_name}` is invalid")]
845 #[diagnostic(code(nu::shell::plugin_registry_data_invalid))]
846 PluginRegistryDataInvalid {
847 plugin_name: String,
848 #[label("plugin `{plugin_name}` loaded here")]
849 span: Option<Span>,
850 #[help(
851 "the format in the plugin registry file is not compatible with this version of Nushell.\n\nTry adding the plugin again with `{}`"
852 )]
853 add_command: String,
854 },
855
856 #[error("Plugin failed to load: {msg}")]
862 #[diagnostic(code(nu::shell::plugin_failed_to_load))]
863 PluginFailedToLoad { msg: String },
864
865 #[error("Plugin failed to encode: {msg}")]
871 #[diagnostic(code(nu::shell::plugin_failed_to_encode))]
872 PluginFailedToEncode { msg: String },
873
874 #[error("Plugin failed to decode: {msg}")]
880 #[diagnostic(code(nu::shell::plugin_failed_to_decode))]
881 PluginFailedToDecode { msg: String },
882
883 #[error("Custom value `{name}` cannot be sent to plugin")]
890 #[diagnostic(code(nu::shell::custom_value_incorrect_for_plugin))]
891 CustomValueIncorrectForPlugin {
892 name: String,
893 #[label("the `{dest_plugin}` plugin does not support this kind of value")]
894 span: Span,
895 dest_plugin: String,
896 #[help("this value came from the `{}` plugin")]
897 src_plugin: Option<String>,
898 },
899
900 #[error("Custom value failed to encode")]
907 #[diagnostic(code(nu::shell::custom_value_failed_to_encode))]
908 CustomValueFailedToEncode {
909 msg: String,
910 #[label("{msg}")]
911 span: Span,
912 },
913
914 #[error("Custom value failed to decode")]
921 #[diagnostic(code(nu::shell::custom_value_failed_to_decode))]
922 #[diagnostic(help("the plugin may have been updated and no longer support this custom value"))]
923 CustomValueFailedToDecode {
924 msg: String,
925 #[label("{msg}")]
926 span: Span,
927 },
928
929 #[error(transparent)]
935 #[diagnostic(transparent)]
936 Io(#[from] io::IoError),
937
938 #[error("Name not found")]
944 #[diagnostic(code(nu::shell::name_not_found))]
945 DidYouMean {
946 suggestion: String,
947 #[label("did you mean '{suggestion}'?")]
948 span: Span,
949 },
950
951 #[error("{msg}")]
957 #[diagnostic(code(nu::shell::did_you_mean_custom))]
958 DidYouMeanCustom {
959 msg: String,
960 suggestion: String,
961 #[label("did you mean '{suggestion}'?")]
962 span: Span,
963 },
964
965 #[error("Non-UTF8 string")]
971 #[diagnostic(
972 code(nu::parser::non_utf8),
973 help("see `decode` for handling character sets other than UTF-8")
974 )]
975 NonUtf8 {
976 #[label("non-UTF8 string")]
977 span: Span,
978 },
979
980 #[error("Non-UTF8 string")]
986 #[diagnostic(
987 code(nu::parser::non_utf8_custom),
988 help("see `decode` for handling character sets other than UTF-8")
989 )]
990 NonUtf8Custom {
991 msg: String,
992 #[label("{msg}")]
993 span: Span,
994 },
995
996 #[error("Encountered {} error(s) when updating config", errors.len())]
1002 #[diagnostic(code(nu::shell::invalid_config))]
1003 InvalidConfig {
1004 #[related]
1005 errors: Vec<ConfigError>,
1006 },
1007
1008 #[error("Value is missing a required '{column}' column")]
1014 #[diagnostic(code(nu::shell::missing_required_column))]
1015 MissingRequiredColumn {
1016 column: &'static str,
1017 #[label("has no '{column}' column")]
1018 span: Span,
1019 },
1020
1021 #[error("Negative value passed when positive one is required")]
1027 #[diagnostic(code(nu::shell::needs_positive_value))]
1028 NeedsPositiveValue {
1029 #[label("use a positive value")]
1030 span: Span,
1031 },
1032
1033 #[error("{error}")]
1035 #[diagnostic()]
1036 GenericError {
1037 error: String,
1038 msg: String,
1039 #[label("{msg}")]
1040 span: Option<Span>,
1041 #[help]
1042 help: Option<String>,
1043 #[related]
1044 inner: Vec<ShellError>,
1045 },
1046
1047 #[error("{error}")]
1049 #[diagnostic()]
1050 OutsideSpannedLabeledError {
1051 #[source_code]
1052 src: String,
1053 error: String,
1054 msg: String,
1055 #[label("{msg}")]
1056 span: Span,
1057 },
1058
1059 #[error(transparent)]
1061 #[diagnostic(transparent)]
1062 LabeledError(#[from] Box<super::LabeledError>),
1063
1064 #[error("Removed command: {removed}")]
1070 #[diagnostic(code(nu::shell::removed_command))]
1071 RemovedCommand {
1072 removed: String,
1073 replacement: String,
1074 #[label("'{removed}' has been removed from Nushell. Please use '{replacement}' instead.")]
1075 span: Span,
1076 },
1077
1078 #[error("Eval block failed with pipeline input")]
1081 #[diagnostic(code(nu::shell::eval_block_with_input))]
1082 EvalBlockWithInput {
1083 #[label("source value")]
1084 span: Span,
1085 #[related]
1086 sources: Vec<ShellError>,
1087 },
1088
1089 #[error("Break used outside of loop")]
1091 Break {
1092 #[label("used outside of loop")]
1093 span: Span,
1094 },
1095
1096 #[error("Continue used outside of loop")]
1098 Continue {
1099 #[label("used outside of loop")]
1100 span: Span,
1101 },
1102
1103 #[error("Return used outside of custom command or closure")]
1105 Return {
1106 #[label("used outside of custom command or closure")]
1107 span: Span,
1108 value: Box<Value>,
1109 },
1110
1111 #[error("Recursion limit ({recursion_limit}) reached")]
1117 #[diagnostic(code(nu::shell::recursion_limit_reached))]
1118 RecursionLimitReached {
1119 recursion_limit: u64,
1120 #[label("This called itself too many times")]
1121 span: Option<Span>,
1122 },
1123
1124 #[error("Operation interrupted")]
1126 Interrupted {
1127 #[label("This operation was interrupted")]
1128 span: Span,
1129 },
1130
1131 #[error("Match guard not bool")]
1134 #[diagnostic(
1135 code(nu::shell::match_guard_not_bool),
1136 help("Match guards should evaluate to a boolean")
1137 )]
1138 MatchGuardNotBool {
1139 #[label("not a boolean expression")]
1140 span: Span,
1141 },
1142
1143 #[error("Missing const eval implementation")]
1148 #[diagnostic(
1149 code(nu::shell::missing_const_eval_implementation),
1150 help(
1151 "The command lacks an implementation for constant evaluation. \
1152This is an internal Nushell error, please file an issue https://github.com/nushell/nushell/issues."
1153 )
1154 )]
1155 MissingConstEvalImpl {
1156 #[label("command lacks constant implementation")]
1157 span: Span,
1158 },
1159
1160 #[error("Found parsing error in expression.")]
1168 #[diagnostic(
1169 code(nu::shell::parse_error_in_constant),
1170 help(
1171 "This expression is supposed to be evaluated into a constant, which means error-free."
1172 )
1173 )]
1174 ParseErrorInConstant {
1175 #[label("Parsing error detected in expression")]
1176 span: Span,
1177 },
1178
1179 #[error("Not a constant.")]
1185 #[diagnostic(
1186 code(nu::shell::not_a_constant),
1187 help(
1188 "Only a subset of expressions are allowed constants during parsing. Try using the 'const' command or typing the value literally."
1189 )
1190 )]
1191 NotAConstant {
1192 #[label("Value is not a parse-time constant")]
1193 span: Span,
1194 },
1195
1196 #[error("Not a const command.")]
1203 #[diagnostic(
1204 code(nu::shell::not_a_const_command),
1205 help("Only a subset of builtin commands can run at parse time.")
1206 )]
1207 NotAConstCommand {
1208 #[label("This command cannot run at parse time.")]
1209 span: Span,
1210 },
1211
1212 #[error("Help message not a constant.")]
1218 #[diagnostic(
1219 code(nu::shell::not_a_const_help),
1220 help("Help messages are currently not supported to be constants.")
1221 )]
1222 NotAConstHelp {
1223 #[label("This command cannot run at parse time.")]
1224 span: Span,
1225 },
1226
1227 #[error("{deprecation_type} deprecated.")]
1228 #[diagnostic(code(nu::shell::deprecated), severity(Warning))]
1229 DeprecationWarning {
1230 deprecation_type: &'static str,
1231 suggestion: String,
1232 #[label("{suggestion}")]
1233 span: Span,
1234 #[help]
1235 help: Option<&'static str>,
1236 },
1237
1238 #[error("Invalid glob pattern")]
1244 #[diagnostic(
1245 code(nu::shell::invalid_glob_pattern),
1246 help("Refer to xxx for help on nushell glob patterns.")
1247 )]
1248 InvalidGlobPattern {
1249 msg: String,
1250 #[label("{msg}")]
1251 span: Span,
1252 },
1253
1254 #[error("Invalid unit")]
1260 #[diagnostic(
1261 code(nu::shell::invalid_unit),
1262 help("Supported units are: {supported_units}")
1263 )]
1264 InvalidUnit {
1265 supported_units: String,
1266 #[label("encountered here")]
1267 span: Span,
1268 },
1269
1270 #[error("Not a list")]
1276 #[diagnostic(
1277 code(nu::shell::cannot_spread_as_list),
1278 help(
1279 "Only lists can be spread inside lists and command calls. Try converting the value to a list before spreading."
1280 )
1281 )]
1282 CannotSpreadAsList {
1283 #[label = "cannot spread value"]
1284 span: Span,
1285 },
1286
1287 #[error("Not a record")]
1293 #[diagnostic(
1294 code(nu::shell::cannot_spread_as_record),
1295 help(
1296 "Only records can be spread inside records. Try converting the value to a record before spreading."
1297 )
1298 )]
1299 CannotSpreadAsRecord {
1300 #[label = "cannot spread value"]
1301 span: Span,
1302 },
1303
1304 #[error("Lists are not automatically spread when calling external commands")]
1310 #[diagnostic(
1311 code(nu::shell::cannot_pass_list_to_external),
1312 help("Either convert the list to a string or use the spread operator, like so: ...{arg}")
1313 )]
1314 CannotPassListToExternal {
1315 arg: String,
1316 #[label = "Spread operator (...) is necessary to spread lists"]
1317 span: Span,
1318 },
1319
1320 #[error(
1326 "The selected range {left_flank}..{right_flank} is out of the bounds of the provided input"
1327 )]
1328 #[diagnostic(code(nu::shell::out_of_bounds))]
1329 OutOfBounds {
1330 left_flank: String,
1331 right_flank: String,
1332 #[label = "byte index is not a char boundary or is out of bounds of the input"]
1333 span: Span,
1334 },
1335
1336 #[error("The config directory could not be found")]
1338 #[diagnostic(
1339 code(nu::shell::config_dir_not_found),
1340 help(
1341 r#"On Linux, this would be $XDG_CONFIG_HOME or $HOME/.config.
1342On MacOS, this would be `$HOME/Library/Application Support`.
1343On Windows, this would be %USERPROFILE%\AppData\Roaming"#
1344 )
1345 )]
1346 ConfigDirNotFound {
1347 #[label = "Could not find config directory"]
1348 span: Span,
1349 },
1350
1351 #[error(
1353 "$env.XDG_CONFIG_HOME ({xdg}) is invalid, using default config directory instead: {default}"
1354 )]
1355 #[diagnostic(
1356 code(nu::shell::xdg_config_home_invalid),
1357 help("Set XDG_CONFIG_HOME to an absolute path, or set it to an empty string to ignore it")
1358 )]
1359 InvalidXdgConfig { xdg: String, default: String },
1360
1361 #[error("IR evaluation error: {msg}")]
1368 #[diagnostic(
1369 code(nu::shell::ir_eval_error),
1370 help(
1371 "this is a bug, please report it at https://github.com/nushell/nushell/issues/new along with the code you were running if able"
1372 )
1373 )]
1374 IrEvalError {
1375 msg: String,
1376 #[label = "while running this code"]
1377 span: Option<Span>,
1378 },
1379
1380 #[error("OS feature is disabled: {msg}")]
1381 #[diagnostic(
1382 code(nu::shell::os_disabled),
1383 help("You're probably running outside an OS like a browser, we cannot support this")
1384 )]
1385 DisabledOsSupport {
1386 msg: String,
1387 #[label = "while running this code"]
1388 span: Span,
1389 },
1390
1391 #[error(transparent)]
1392 #[diagnostic(transparent)]
1393 Job(#[from] JobError),
1394
1395 #[error(transparent)]
1396 #[diagnostic(transparent)]
1397 ChainedError(ChainedError),
1398}
1399
1400impl ShellError {
1401 pub fn external_exit_code(&self) -> Option<Spanned<i32>> {
1402 let (item, span) = match *self {
1403 Self::NonZeroExitCode { exit_code, span } => (exit_code.into(), span),
1404 #[cfg(unix)]
1405 Self::TerminatedBySignal { signal, span, .. }
1406 | Self::CoreDumped { signal, span, .. } => (-signal, span),
1407 _ => return None,
1408 };
1409 Some(Spanned { item, span })
1410 }
1411
1412 pub fn exit_code(&self) -> Option<i32> {
1413 match self {
1414 Self::Return { .. } | Self::Break { .. } | Self::Continue { .. } => None,
1415 _ => self.external_exit_code().map(|e| e.item).or(Some(1)),
1416 }
1417 }
1418
1419 pub fn into_value(self, working_set: &StateWorkingSet, span: Span) -> Value {
1420 let exit_code = self.external_exit_code();
1421
1422 let mut record = record! {
1423 "msg" => Value::string(self.to_string(), span),
1424 "debug" => Value::string(format!("{self:?}"), span),
1425 "raw" => Value::error(self.clone(), span),
1426 "rendered" => Value::string(format_cli_error(working_set, &self, Some("nu::shell::error")), span),
1427 "json" => Value::string(serde_json::to_string(&self).expect("Could not serialize error"), span),
1428 };
1429
1430 if let Some(code) = exit_code {
1431 record.push("exit_code", Value::int(code.item.into(), code.span));
1432 }
1433
1434 Value::record(record, span)
1435 }
1436
1437 pub fn wrap(self, working_set: &StateWorkingSet, span: Span) -> ParseError {
1439 let msg = format_cli_error(working_set, &self, None);
1440 ParseError::LabeledError(
1441 msg,
1442 "Encountered error during parse-time evaluation".into(),
1443 span,
1444 )
1445 }
1446
1447 pub fn into_chainned(self, span: Span) -> Self {
1449 match self {
1450 ShellError::ChainedError(inner) => {
1451 ShellError::ChainedError(ChainedError::new_chained(inner, span))
1452 }
1453 other => ShellError::ChainedError(ChainedError::new(other, span)),
1454 }
1455 }
1456}
1457
1458impl From<Box<dyn std::error::Error>> for ShellError {
1459 fn from(error: Box<dyn std::error::Error>) -> ShellError {
1460 ShellError::GenericError {
1461 error: format!("{error:?}"),
1462 msg: error.to_string(),
1463 span: None,
1464 help: None,
1465 inner: vec![],
1466 }
1467 }
1468}
1469
1470impl From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
1471 fn from(error: Box<dyn std::error::Error + Send + Sync>) -> ShellError {
1472 ShellError::GenericError {
1473 error: format!("{error:?}"),
1474 msg: error.to_string(),
1475 span: None,
1476 help: None,
1477 inner: vec![],
1478 }
1479 }
1480}
1481
1482impl From<super::LabeledError> for ShellError {
1483 fn from(error: super::LabeledError) -> Self {
1484 ShellError::LabeledError(Box::new(error))
1485 }
1486}
1487
1488impl Serialize for ShellError {
1490 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1491 where
1492 S: serde::Serializer,
1493 {
1494 LabeledError::from_diagnostic(self).serialize(serializer)
1495 }
1496}
1497
1498impl<'de> Deserialize<'de> for ShellError {
1501 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1502 where
1503 D: serde::Deserializer<'de>,
1504 {
1505 LabeledError::deserialize(deserializer).map(ShellError::from)
1506 }
1507}
1508
1509#[test]
1510fn shell_error_serialize_roundtrip() {
1511 let original_error = ShellError::CantConvert {
1514 span: Span::new(100, 200),
1515 to_type: "Foo".into(),
1516 from_type: "Bar".into(),
1517 help: Some("this is a test".into()),
1518 };
1519 println!("orig_error = {original_error:#?}");
1520
1521 let serialized =
1522 serde_json::to_string_pretty(&original_error).expect("serde_json::to_string_pretty failed");
1523 println!("serialized = {serialized}");
1524
1525 let deserialized: ShellError =
1526 serde_json::from_str(&serialized).expect("serde_json::from_str failed");
1527 println!("deserialized = {deserialized:#?}");
1528
1529 assert_eq!(original_error.to_string(), deserialized.to_string());
1532
1533 assert_eq!(
1534 original_error.code().map(|c| c.to_string()),
1535 deserialized.code().map(|c| c.to_string())
1536 );
1537
1538 let orig_labels = original_error
1539 .labels()
1540 .into_iter()
1541 .flatten()
1542 .collect::<Vec<_>>();
1543 let deser_labels = deserialized
1544 .labels()
1545 .into_iter()
1546 .flatten()
1547 .collect::<Vec<_>>();
1548
1549 assert_eq!(orig_labels, deser_labels);
1550
1551 assert_eq!(
1552 original_error.help().map(|c| c.to_string()),
1553 deserialized.help().map(|c| c.to_string())
1554 );
1555}
1556
1557#[cfg(test)]
1558mod test {
1559 use super::*;
1560
1561 impl From<std::io::Error> for ShellError {
1562 fn from(_: std::io::Error) -> ShellError {
1563 unimplemented!(
1564 "This implementation is defined in the test module to ensure no other implementation exists."
1565 )
1566 }
1567 }
1568
1569 impl From<Spanned<std::io::Error>> for ShellError {
1570 fn from(_: Spanned<std::io::Error>) -> Self {
1571 unimplemented!(
1572 "This implementation is defined in the test module to ensure no other implementation exists."
1573 )
1574 }
1575 }
1576
1577 impl From<ShellError> for std::io::Error {
1578 fn from(_: ShellError) -> Self {
1579 unimplemented!(
1580 "This implementation is defined in the test module to ensure no other implementation exists."
1581 )
1582 }
1583 }
1584}