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;
16pub mod network;
17
18#[derive(Debug, Clone, Error, Diagnostic, PartialEq)]
22pub enum ShellError {
23 #[error("The '{op}' operator does not work on values of type '{unsupported}'.")]
25 #[diagnostic(code(nu::shell::operator_unsupported_type))]
26 OperatorUnsupportedType {
27 op: Operator,
28 unsupported: Type,
29 #[label = "does not support '{unsupported}'"]
30 op_span: Span,
31 #[label("{unsupported}")]
32 unsupported_span: Span,
33 #[help]
34 help: Option<&'static str>,
35 },
36
37 #[error("Types '{lhs}' and '{rhs}' are not compatible for the '{op}' operator.")]
39 #[diagnostic(code(nu::shell::operator_incompatible_types))]
40 OperatorIncompatibleTypes {
41 op: Operator,
42 lhs: Type,
43 rhs: Type,
44 #[label = "does not operate between '{lhs}' and '{rhs}'"]
45 op_span: Span,
46 #[label("{lhs}")]
47 lhs_span: Span,
48 #[label("{rhs}")]
49 rhs_span: Span,
50 #[help]
51 help: Option<&'static str>,
52 },
53
54 #[error("Operator overflow.")]
61 #[diagnostic(code(nu::shell::operator_overflow))]
62 OperatorOverflow {
63 msg: String,
64 #[label = "{msg}"]
65 span: Span,
66 #[help]
67 help: Option<String>,
68 },
69
70 #[error("Pipeline mismatch.")]
77 #[diagnostic(code(nu::shell::pipeline_mismatch))]
78 PipelineMismatch {
79 exp_input_type: String,
80 #[label("expected: {exp_input_type}")]
81 dst_span: Span,
82 #[label("value originates here")]
83 src_span: Span,
84 },
85
86 #[error("Input type not supported.")]
96 #[diagnostic(code(nu::shell::only_supports_this_input_type))]
97 OnlySupportsThisInputType {
98 exp_input_type: String,
99 wrong_type: String,
100 #[label("only {exp_input_type} input data is supported")]
101 dst_span: Span,
102 #[label("input type: {wrong_type}")]
103 src_span: Span,
104 },
105
106 #[error("Pipeline empty.")]
112 #[diagnostic(code(nu::shell::pipeline_mismatch))]
113 PipelineEmpty {
114 #[label("no input value was piped in")]
115 dst_span: Span,
116 },
117
118 #[error("Type mismatch.")]
125 #[diagnostic(code(nu::shell::type_mismatch))]
126 TypeMismatch {
127 err_message: String,
128 #[label = "{err_message}"]
129 span: Span,
130 },
131
132 #[error("Type mismatch")]
138 #[diagnostic(code(nu::shell::type_mismatch))]
139 RuntimeTypeMismatch {
140 expected: Type,
141 actual: Type,
142 #[label = "expected {expected}, but got {actual}"]
143 span: Span,
144 },
145
146 #[error("Invalid value")]
152 #[diagnostic(code(nu::shell::invalid_value))]
153 InvalidValue {
154 valid: String,
155 actual: String,
156 #[label = "expected {valid}, but got {actual}"]
157 span: Span,
158 },
159
160 #[error("Incorrect value.")]
166 #[diagnostic(code(nu::shell::incorrect_value))]
167 IncorrectValue {
168 msg: String,
169 #[label = "{msg}"]
170 val_span: Span,
171 #[label = "encountered here"]
172 call_span: Span,
173 },
174
175 #[error("Assignment operations require a variable.")]
181 #[diagnostic(code(nu::shell::assignment_requires_variable))]
182 AssignmentRequiresVar {
183 #[label = "needs to be a variable"]
184 lhs_span: Span,
185 },
186
187 #[error("Assignment to an immutable variable.")]
193 #[diagnostic(code(nu::shell::assignment_requires_mutable_variable))]
194 AssignmentRequiresMutableVar {
195 #[label = "needs to be a mutable variable"]
196 lhs_span: Span,
197 },
198
199 #[error("Unknown operator: {op_token}.")]
205 #[diagnostic(code(nu::shell::unknown_operator))]
206 UnknownOperator {
207 op_token: String,
208 #[label = "unknown operator"]
209 span: Span,
210 },
211
212 #[error("Missing parameter: {param_name}.")]
218 #[diagnostic(code(nu::shell::missing_parameter))]
219 MissingParameter {
220 param_name: String,
221 #[label = "missing parameter: {param_name}"]
222 span: Span,
223 },
224
225 #[error("Incompatible parameters.")]
231 #[diagnostic(code(nu::shell::incompatible_parameters))]
232 IncompatibleParameters {
233 left_message: String,
234 #[label("{left_message}")]
236 left_span: Span,
237 right_message: String,
238 #[label("{right_message}")]
239 right_span: Span,
240 },
241
242 #[error("Delimiter error")]
248 #[diagnostic(code(nu::shell::delimiter_error))]
249 DelimiterError {
250 msg: String,
251 #[label("{msg}")]
252 span: Span,
253 },
254
255 #[error("Incompatible parameters.")]
263 #[diagnostic(code(nu::shell::incompatible_parameters))]
264 IncompatibleParametersSingle {
265 msg: String,
266 #[label = "{msg}"]
267 span: Span,
268 },
269
270 #[error("Running external commands not supported")]
276 #[diagnostic(code(nu::shell::external_commands))]
277 ExternalNotSupported {
278 #[label = "external not supported"]
279 span: Span,
280 },
281
282 #[error("Invalid Probability.")]
289 #[diagnostic(code(nu::shell::invalid_probability))]
290 InvalidProbability {
291 #[label = "invalid probability: must be between 0 and 1"]
292 span: Span,
293 },
294
295 #[error("Invalid range {left_flank}..{right_flank}")]
301 #[diagnostic(code(nu::shell::invalid_range))]
302 InvalidRange {
303 left_flank: String,
304 right_flank: String,
305 #[label = "expected a valid range"]
306 span: Span,
307 },
308
309 #[error("Nushell failed: {msg}.")]
315 #[diagnostic(
316 code(nu::shell::nushell_failed),
317 help(
318 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
319 )
320 )]
321 NushellFailed { msg: String },
323
324 #[error("Nushell failed: {msg}.")]
330 #[diagnostic(
331 code(nu::shell::nushell_failed_spanned),
332 help(
333 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
334 )
335 )]
336 NushellFailedSpanned {
338 msg: String,
339 label: String,
340 #[label = "{label}"]
341 span: Span,
342 },
343
344 #[error("Nushell failed: {msg}.")]
350 #[diagnostic(code(nu::shell::nushell_failed_help))]
351 NushellFailedHelp {
353 msg: String,
354 #[help]
355 help: String,
356 },
357
358 #[error("Variable not found")]
364 #[diagnostic(code(nu::shell::variable_not_found))]
365 VariableNotFoundAtRuntime {
366 #[label = "variable not found"]
367 span: Span,
368 },
369
370 #[error("Environment variable '{envvar_name}' not found")]
376 #[diagnostic(code(nu::shell::env_variable_not_found))]
377 EnvVarNotFoundAtRuntime {
378 envvar_name: String,
379 #[label = "environment variable not found"]
380 span: Span,
381 },
382
383 #[error("Module '{mod_name}' not found")]
389 #[diagnostic(code(nu::shell::module_not_found))]
390 ModuleNotFoundAtRuntime {
391 mod_name: String,
392 #[label = "module not found"]
393 span: Span,
394 },
395
396 #[error("Overlay '{overlay_name}' not found")]
402 #[diagnostic(code(nu::shell::overlay_not_found))]
403 OverlayNotFoundAtRuntime {
404 overlay_name: String,
405 #[label = "overlay not found"]
406 span: Span,
407 },
408
409 #[error("Not found.")]
415 #[diagnostic(code(nu::parser::not_found))]
416 NotFound {
417 #[label = "did not find anything under this name"]
418 span: Span,
419 },
420
421 #[error("Can't convert to {to_type}.")]
427 #[diagnostic(code(nu::shell::cant_convert))]
428 CantConvert {
429 to_type: String,
430 from_type: String,
431 #[label("can't convert {from_type} to {to_type}")]
432 span: Span,
433 #[help]
434 help: Option<String>,
435 },
436
437 #[error("Can't convert {from_type} to the specified unit.")]
443 #[diagnostic(code(nu::shell::cant_convert_value_to_unit))]
444 CantConvertToUnit {
445 to_type: String,
446 from_type: String,
447 #[label("can't convert {from_type} to {to_type}")]
448 span: Span,
449 #[label("conversion originates here")]
450 unit_span: Span,
451 #[help]
452 help: Option<String>,
453 },
454
455 #[error("'{envvar_name}' is not representable as a string.")]
461 #[diagnostic(
462 code(nu::shell::env_var_not_a_string),
463 help(
464 r#"The '{envvar_name}' environment variable must be a string or be convertible to a string.
465 Either make sure '{envvar_name}' is a string, or add a 'to_string' entry for it in ENV_CONVERSIONS."#
466 )
467 )]
468 EnvVarNotAString {
469 envvar_name: String,
470 #[label("value not representable as a string")]
471 span: Span,
472 },
473
474 #[error("{envvar_name} cannot be set manually.")]
480 #[diagnostic(
481 code(nu::shell::automatic_env_var_set_manually),
482 help(
483 r#"The environment variable '{envvar_name}' is set automatically by Nushell and cannot be set manually."#
484 )
485 )]
486 AutomaticEnvVarSetManually {
487 envvar_name: String,
488 #[label("cannot set '{envvar_name}' manually")]
489 span: Span,
490 },
491
492 #[error("Cannot replace environment.")]
499 #[diagnostic(
500 code(nu::shell::cannot_replace_env),
501 help(r#"Assigning a value to '$env' is not allowed."#)
502 )]
503 CannotReplaceEnv {
504 #[label("setting '$env' not allowed")]
505 span: Span,
506 },
507
508 #[error("Division by zero.")]
514 #[diagnostic(code(nu::shell::division_by_zero))]
515 DivisionByZero {
516 #[label("division by zero")]
517 span: Span,
518 },
519
520 #[error("Can't convert range to countable values")]
528 #[diagnostic(code(nu::shell::range_to_countable))]
529 CannotCreateRange {
530 #[label = "can't convert to countable values"]
531 span: Span,
532 },
533
534 #[error("Row number too large (max: {max_idx}).")]
540 #[diagnostic(code(nu::shell::access_beyond_end))]
541 AccessBeyondEnd {
542 max_idx: usize,
543 #[label = "index too large (max: {max_idx})"]
544 span: Span,
545 },
546
547 #[error("Inserted at wrong row number (should be {available_idx}).")]
553 #[diagnostic(code(nu::shell::access_beyond_end))]
554 InsertAfterNextFreeIndex {
555 available_idx: usize,
556 #[label = "can't insert at index (the next available index is {available_idx})"]
557 span: Span,
558 },
559
560 #[error("Row number too large (empty content).")]
566 #[diagnostic(code(nu::shell::access_beyond_end))]
567 AccessEmptyContent {
568 #[label = "index too large (empty content)"]
569 span: Span,
570 },
571
572 #[error("Row number too large.")]
579 #[diagnostic(code(nu::shell::access_beyond_end_of_stream))]
580 AccessBeyondEndOfStream {
581 #[label = "index too large"]
582 span: Span,
583 },
584
585 #[error("Data cannot be accessed with a cell path")]
591 #[diagnostic(code(nu::shell::incompatible_path_access))]
592 IncompatiblePathAccess {
593 type_name: String,
594 #[label("{type_name} doesn't support cell paths")]
595 span: Span,
596 },
597
598 #[error("Cannot find column '{col_name}'")]
604 #[diagnostic(code(nu::shell::column_not_found))]
605 CantFindColumn {
606 col_name: String,
607 #[label = "cannot find column '{col_name}'"]
608 span: Option<Span>,
609 #[label = "value originates here"]
610 src_span: Span,
611 },
612
613 #[error("Column already exists")]
619 #[diagnostic(code(nu::shell::column_already_exists))]
620 ColumnAlreadyExists {
621 col_name: String,
622 #[label = "column '{col_name}' already exists"]
623 span: Span,
624 #[label = "value originates here"]
625 src_span: Span,
626 },
627
628 #[error("Not a list value")]
634 #[diagnostic(code(nu::shell::not_a_list))]
635 NotAList {
636 #[label = "value not a list"]
637 dst_span: Span,
638 #[label = "value originates here"]
639 src_span: Span,
640 },
641
642 #[error("Record field or table column used twice: {col_name}")]
648 #[diagnostic(code(nu::shell::column_defined_twice))]
649 ColumnDefinedTwice {
650 col_name: String,
651 #[label = "field redefined here"]
652 second_use: Span,
653 #[label = "field first defined here"]
654 first_use: Span,
655 },
656
657 #[error("Attempted to create a record from different number of columns and values")]
663 #[diagnostic(code(nu::shell::record_cols_vals_mismatch))]
664 RecordColsValsMismatch {
665 #[label = "problematic value"]
666 bad_value: Span,
667 #[label = "attempted to create the record here"]
668 creation_site: Span,
669 },
670
671 #[error("Failed to detect columns")]
677 #[diagnostic(code(nu::shell::failed_to_detect_columns))]
678 ColumnDetectionFailure {
679 #[label = "value coming from here"]
680 bad_value: Span,
681 #[label = "tried to detect columns here"]
682 failure_site: Span,
683 },
684
685 #[error("Relative range values cannot be used with streams that don't have a known length")]
691 #[diagnostic(code(nu::shell::relative_range_on_infinite_stream))]
692 RelativeRangeOnInfiniteStream {
693 #[label = "Relative range values cannot be used with streams that don't have a known length"]
694 span: Span,
695 },
696
697 #[error("External command failed")]
703 #[diagnostic(code(nu::shell::external_command), help("{help}"))]
704 ExternalCommand {
705 label: String,
706 help: String,
707 #[label("{label}")]
708 span: Span,
709 },
710
711 #[error("External command had a non-zero exit code")]
717 #[diagnostic(code(nu::shell::non_zero_exit_code))]
718 NonZeroExitCode {
719 exit_code: NonZeroI32,
720 #[label("exited with code {exit_code}")]
721 span: Span,
722 },
723
724 #[cfg(unix)]
725 #[error("External command was terminated by a signal")]
731 #[diagnostic(code(nu::shell::terminated_by_signal))]
732 TerminatedBySignal {
733 signal_name: String,
734 signal: i32,
735 #[label("terminated by {signal_name} ({signal})")]
736 span: Span,
737 },
738
739 #[cfg(unix)]
740 #[error("External command core dumped")]
746 #[diagnostic(code(nu::shell::core_dumped))]
747 CoreDumped {
748 signal_name: String,
749 signal: i32,
750 #[label("core dumped with {signal_name} ({signal})")]
751 span: Span,
752 },
753
754 #[error("Unsupported input")]
760 #[diagnostic(code(nu::shell::unsupported_input))]
761 UnsupportedInput {
762 msg: String,
763 input: String,
764 #[label("{msg}")]
765 msg_span: Span,
766 #[label("{input}")]
767 input_span: Span,
768 },
769
770 #[error("Unable to parse datetime: [{msg}].")]
785 #[diagnostic(
786 code(nu::shell::datetime_parse_error),
787 help(
788 r#"Examples of supported inputs:
789 * "5 pm"
790 * "2020/12/4"
791 * "2020.12.04 22:10 +2"
792 * "2020-04-12 22:10:57 +02:00"
793 * "2020-04-12T22:10:57.213231+02:00"
794 * "Tue, 1 Jul 2003 10:52:37 +0200""#
795 )
796 )]
797 DatetimeParseError {
798 msg: String,
799 #[label("datetime parsing failed")]
800 span: Span,
801 },
802
803 #[error("Network failure")]
809 #[diagnostic(code(nu::shell::network_failure))]
810 NetworkFailure {
811 msg: String,
812 #[label("{msg}")]
813 span: Span,
814 },
815
816 #[error(transparent)]
817 #[diagnostic(transparent)]
818 Network(#[from] network::NetworkError),
819
820 #[error("Command not found")]
826 #[diagnostic(code(nu::shell::command_not_found))]
827 CommandNotFound {
828 #[label("command not found")]
829 span: Span,
830 },
831
832 #[error("Alias not found")]
838 #[diagnostic(code(nu::shell::alias_not_found))]
839 AliasNotFound {
840 #[label("alias not found")]
841 span: Span,
842 },
843
844 #[error("The registered plugin data for `{plugin_name}` is invalid")]
850 #[diagnostic(code(nu::shell::plugin_registry_data_invalid))]
851 PluginRegistryDataInvalid {
852 plugin_name: String,
853 #[label("plugin `{plugin_name}` loaded here")]
854 span: Option<Span>,
855 #[help(
856 "the format in the plugin registry file is not compatible with this version of Nushell.\n\nTry adding the plugin again with `{}`"
857 )]
858 add_command: String,
859 },
860
861 #[error("Plugin failed to load: {msg}")]
867 #[diagnostic(code(nu::shell::plugin_failed_to_load))]
868 PluginFailedToLoad { msg: String },
869
870 #[error("Plugin failed to encode: {msg}")]
876 #[diagnostic(code(nu::shell::plugin_failed_to_encode))]
877 PluginFailedToEncode { msg: String },
878
879 #[error("Plugin failed to decode: {msg}")]
885 #[diagnostic(code(nu::shell::plugin_failed_to_decode))]
886 PluginFailedToDecode { msg: String },
887
888 #[error("Custom value `{name}` cannot be sent to plugin")]
895 #[diagnostic(code(nu::shell::custom_value_incorrect_for_plugin))]
896 CustomValueIncorrectForPlugin {
897 name: String,
898 #[label("the `{dest_plugin}` plugin does not support this kind of value")]
899 span: Span,
900 dest_plugin: String,
901 #[help("this value came from the `{}` plugin")]
902 src_plugin: Option<String>,
903 },
904
905 #[error("Custom value failed to encode")]
912 #[diagnostic(code(nu::shell::custom_value_failed_to_encode))]
913 CustomValueFailedToEncode {
914 msg: String,
915 #[label("{msg}")]
916 span: Span,
917 },
918
919 #[error("Custom value failed to decode")]
926 #[diagnostic(code(nu::shell::custom_value_failed_to_decode))]
927 #[diagnostic(help("the plugin may have been updated and no longer support this custom value"))]
928 CustomValueFailedToDecode {
929 msg: String,
930 #[label("{msg}")]
931 span: Span,
932 },
933
934 #[error(transparent)]
940 #[diagnostic(transparent)]
941 Io(#[from] io::IoError),
942
943 #[error("Name not found")]
949 #[diagnostic(code(nu::shell::name_not_found))]
950 DidYouMean {
951 suggestion: String,
952 #[label("did you mean '{suggestion}'?")]
953 span: Span,
954 },
955
956 #[error("{msg}")]
962 #[diagnostic(code(nu::shell::did_you_mean_custom))]
963 DidYouMeanCustom {
964 msg: String,
965 suggestion: String,
966 #[label("did you mean '{suggestion}'?")]
967 span: Span,
968 },
969
970 #[error("Non-UTF8 string")]
976 #[diagnostic(
977 code(nu::parser::non_utf8),
978 help("see `decode` for handling character sets other than UTF-8")
979 )]
980 NonUtf8 {
981 #[label("non-UTF8 string")]
982 span: Span,
983 },
984
985 #[error("Non-UTF8 string")]
991 #[diagnostic(
992 code(nu::parser::non_utf8_custom),
993 help("see `decode` for handling character sets other than UTF-8")
994 )]
995 NonUtf8Custom {
996 msg: String,
997 #[label("{msg}")]
998 span: Span,
999 },
1000
1001 #[error("Encountered {} error(s) when updating config", errors.len())]
1007 #[diagnostic(code(nu::shell::invalid_config))]
1008 InvalidConfig {
1009 #[related]
1010 errors: Vec<ConfigError>,
1011 },
1012
1013 #[error("Value is missing a required '{column}' column")]
1019 #[diagnostic(code(nu::shell::missing_required_column))]
1020 MissingRequiredColumn {
1021 column: &'static str,
1022 #[label("has no '{column}' column")]
1023 span: Span,
1024 },
1025
1026 #[error("Negative value passed when positive one is required")]
1032 #[diagnostic(code(nu::shell::needs_positive_value))]
1033 NeedsPositiveValue {
1034 #[label("use a positive value")]
1035 span: Span,
1036 },
1037
1038 #[error("{error}")]
1040 #[diagnostic()]
1041 GenericError {
1042 error: String,
1043 msg: String,
1044 #[label("{msg}")]
1045 span: Option<Span>,
1046 #[help]
1047 help: Option<String>,
1048 #[related]
1049 inner: Vec<ShellError>,
1050 },
1051
1052 #[error("{error}")]
1054 #[diagnostic()]
1055 OutsideSpannedLabeledError {
1056 #[source_code]
1057 src: String,
1058 error: String,
1059 msg: String,
1060 #[label("{msg}")]
1061 span: Span,
1062 },
1063
1064 #[error(transparent)]
1066 #[diagnostic(transparent)]
1067 LabeledError(#[from] Box<super::LabeledError>),
1068
1069 #[error("Removed command: {removed}")]
1075 #[diagnostic(code(nu::shell::removed_command))]
1076 RemovedCommand {
1077 removed: String,
1078 replacement: String,
1079 #[label("'{removed}' has been removed from Nushell. Please use '{replacement}' instead.")]
1080 span: Span,
1081 },
1082
1083 #[error("Eval block failed with pipeline input")]
1086 #[diagnostic(code(nu::shell::eval_block_with_input))]
1087 EvalBlockWithInput {
1088 #[label("source value")]
1089 span: Span,
1090 #[related]
1091 sources: Vec<ShellError>,
1092 },
1093
1094 #[error("Break used outside of loop")]
1096 Break {
1097 #[label("used outside of loop")]
1098 span: Span,
1099 },
1100
1101 #[error("Continue used outside of loop")]
1103 Continue {
1104 #[label("used outside of loop")]
1105 span: Span,
1106 },
1107
1108 #[error("Return used outside of custom command or closure")]
1110 Return {
1111 #[label("used outside of custom command or closure")]
1112 span: Span,
1113 value: Box<Value>,
1114 },
1115
1116 #[error("Recursion limit ({recursion_limit}) reached")]
1122 #[diagnostic(code(nu::shell::recursion_limit_reached))]
1123 RecursionLimitReached {
1124 recursion_limit: u64,
1125 #[label("This called itself too many times")]
1126 span: Option<Span>,
1127 },
1128
1129 #[error("Operation interrupted")]
1131 Interrupted {
1132 #[label("This operation was interrupted")]
1133 span: Span,
1134 },
1135
1136 #[error("Match guard not bool")]
1139 #[diagnostic(
1140 code(nu::shell::match_guard_not_bool),
1141 help("Match guards should evaluate to a boolean")
1142 )]
1143 MatchGuardNotBool {
1144 #[label("not a boolean expression")]
1145 span: Span,
1146 },
1147
1148 #[error("Missing const eval implementation")]
1153 #[diagnostic(
1154 code(nu::shell::missing_const_eval_implementation),
1155 help(
1156 "The command lacks an implementation for constant evaluation. \
1157This is an internal Nushell error, please file an issue https://github.com/nushell/nushell/issues."
1158 )
1159 )]
1160 MissingConstEvalImpl {
1161 #[label("command lacks constant implementation")]
1162 span: Span,
1163 },
1164
1165 #[error("Found parsing error in expression.")]
1173 #[diagnostic(
1174 code(nu::shell::parse_error_in_constant),
1175 help(
1176 "This expression is supposed to be evaluated into a constant, which means error-free."
1177 )
1178 )]
1179 ParseErrorInConstant {
1180 #[label("Parsing error detected in expression")]
1181 span: Span,
1182 },
1183
1184 #[error("Not a constant.")]
1190 #[diagnostic(
1191 code(nu::shell::not_a_constant),
1192 help(
1193 "Only a subset of expressions are allowed constants during parsing. Try using the 'const' command or typing the value literally."
1194 )
1195 )]
1196 NotAConstant {
1197 #[label("Value is not a parse-time constant")]
1198 span: Span,
1199 },
1200
1201 #[error("Not a const command.")]
1208 #[diagnostic(
1209 code(nu::shell::not_a_const_command),
1210 help("Only a subset of builtin commands can run at parse time.")
1211 )]
1212 NotAConstCommand {
1213 #[label("This command cannot run at parse time.")]
1214 span: Span,
1215 },
1216
1217 #[error("Help message not a constant.")]
1223 #[diagnostic(
1224 code(nu::shell::not_a_const_help),
1225 help("Help messages are currently not supported to be constants.")
1226 )]
1227 NotAConstHelp {
1228 #[label("This command cannot run at parse time.")]
1229 span: Span,
1230 },
1231
1232 #[error("{deprecation_type} deprecated.")]
1233 #[diagnostic(code(nu::shell::deprecated), severity(Warning))]
1234 DeprecationWarning {
1235 deprecation_type: &'static str,
1236 suggestion: String,
1237 #[label("{suggestion}")]
1238 span: Span,
1239 #[help]
1240 help: Option<&'static str>,
1241 },
1242
1243 #[error("Invalid glob pattern")]
1249 #[diagnostic(
1250 code(nu::shell::invalid_glob_pattern),
1251 help("Refer to xxx for help on nushell glob patterns.")
1252 )]
1253 InvalidGlobPattern {
1254 msg: String,
1255 #[label("{msg}")]
1256 span: Span,
1257 },
1258
1259 #[error("Invalid unit")]
1265 #[diagnostic(
1266 code(nu::shell::invalid_unit),
1267 help("Supported units are: {supported_units}")
1268 )]
1269 InvalidUnit {
1270 supported_units: String,
1271 #[label("encountered here")]
1272 span: Span,
1273 },
1274
1275 #[error("Not a list")]
1281 #[diagnostic(
1282 code(nu::shell::cannot_spread_as_list),
1283 help(
1284 "Only lists can be spread inside lists and command calls. Try converting the value to a list before spreading."
1285 )
1286 )]
1287 CannotSpreadAsList {
1288 #[label = "cannot spread value"]
1289 span: Span,
1290 },
1291
1292 #[error("Not a record")]
1298 #[diagnostic(
1299 code(nu::shell::cannot_spread_as_record),
1300 help(
1301 "Only records can be spread inside records. Try converting the value to a record before spreading."
1302 )
1303 )]
1304 CannotSpreadAsRecord {
1305 #[label = "cannot spread value"]
1306 span: Span,
1307 },
1308
1309 #[error("Lists are not automatically spread when calling external commands")]
1315 #[diagnostic(
1316 code(nu::shell::cannot_pass_list_to_external),
1317 help("Either convert the list to a string or use the spread operator, like so: ...{arg}")
1318 )]
1319 CannotPassListToExternal {
1320 arg: String,
1321 #[label = "Spread operator (...) is necessary to spread lists"]
1322 span: Span,
1323 },
1324
1325 #[error(
1331 "The selected range {left_flank}..{right_flank} is out of the bounds of the provided input"
1332 )]
1333 #[diagnostic(code(nu::shell::out_of_bounds))]
1334 OutOfBounds {
1335 left_flank: String,
1336 right_flank: String,
1337 #[label = "byte index is not a char boundary or is out of bounds of the input"]
1338 span: Span,
1339 },
1340
1341 #[error("The config directory could not be found")]
1343 #[diagnostic(
1344 code(nu::shell::config_dir_not_found),
1345 help(
1346 r#"On Linux, this would be $XDG_CONFIG_HOME or $HOME/.config.
1347On MacOS, this would be `$HOME/Library/Application Support`.
1348On Windows, this would be %USERPROFILE%\AppData\Roaming"#
1349 )
1350 )]
1351 ConfigDirNotFound {
1352 #[label = "Could not find config directory"]
1353 span: Span,
1354 },
1355
1356 #[error(
1358 "$env.XDG_CONFIG_HOME ({xdg}) is invalid, using default config directory instead: {default}"
1359 )]
1360 #[diagnostic(
1361 code(nu::shell::xdg_config_home_invalid),
1362 help("Set XDG_CONFIG_HOME to an absolute path, or set it to an empty string to ignore it")
1363 )]
1364 InvalidXdgConfig { xdg: String, default: String },
1365
1366 #[error("IR evaluation error: {msg}")]
1373 #[diagnostic(
1374 code(nu::shell::ir_eval_error),
1375 help(
1376 "this is a bug, please report it at https://github.com/nushell/nushell/issues/new along with the code you were running if able"
1377 )
1378 )]
1379 IrEvalError {
1380 msg: String,
1381 #[label = "while running this code"]
1382 span: Option<Span>,
1383 },
1384
1385 #[error("OS feature is disabled: {msg}")]
1386 #[diagnostic(
1387 code(nu::shell::os_disabled),
1388 help("You're probably running outside an OS like a browser, we cannot support this")
1389 )]
1390 DisabledOsSupport {
1391 msg: String,
1392 #[label = "while running this code"]
1393 span: Span,
1394 },
1395
1396 #[error(transparent)]
1397 #[diagnostic(transparent)]
1398 Job(#[from] JobError),
1399
1400 #[error(transparent)]
1401 #[diagnostic(transparent)]
1402 ChainedError(ChainedError),
1403}
1404
1405impl ShellError {
1406 pub fn external_exit_code(&self) -> Option<Spanned<i32>> {
1407 let (item, span) = match *self {
1408 Self::NonZeroExitCode { exit_code, span } => (exit_code.into(), span),
1409 #[cfg(unix)]
1410 Self::TerminatedBySignal { signal, span, .. }
1411 | Self::CoreDumped { signal, span, .. } => (-signal, span),
1412 _ => return None,
1413 };
1414 Some(Spanned { item, span })
1415 }
1416
1417 pub fn exit_code(&self) -> Option<i32> {
1418 match self {
1419 Self::Return { .. } | Self::Break { .. } | Self::Continue { .. } => None,
1420 _ => self.external_exit_code().map(|e| e.item).or(Some(1)),
1421 }
1422 }
1423
1424 pub fn into_value(self, working_set: &StateWorkingSet, span: Span) -> Value {
1425 let exit_code = self.external_exit_code();
1426
1427 let mut record = record! {
1428 "msg" => Value::string(self.to_string(), span),
1429 "debug" => Value::string(format!("{self:?}"), span),
1430 "raw" => Value::error(self.clone(), span),
1431 "rendered" => Value::string(format_cli_error(working_set, &self, Some("nu::shell::error")), span),
1432 "json" => Value::string(serde_json::to_string(&self).expect("Could not serialize error"), span),
1433 };
1434
1435 if let Some(code) = exit_code {
1436 record.push("exit_code", Value::int(code.item.into(), code.span));
1437 }
1438
1439 Value::record(record, span)
1440 }
1441
1442 pub fn wrap(self, working_set: &StateWorkingSet, span: Span) -> ParseError {
1444 let msg = format_cli_error(working_set, &self, None);
1445 ParseError::LabeledError(
1446 msg,
1447 "Encountered error during parse-time evaluation".into(),
1448 span,
1449 )
1450 }
1451
1452 pub fn into_chainned(self, span: Span) -> Self {
1454 match self {
1455 ShellError::ChainedError(inner) => {
1456 ShellError::ChainedError(ChainedError::new_chained(inner, span))
1457 }
1458 other => ShellError::ChainedError(ChainedError::new(other, span)),
1459 }
1460 }
1461}
1462
1463impl From<Box<dyn std::error::Error>> for ShellError {
1464 fn from(error: Box<dyn std::error::Error>) -> ShellError {
1465 ShellError::GenericError {
1466 error: format!("{error:?}"),
1467 msg: error.to_string(),
1468 span: None,
1469 help: None,
1470 inner: vec![],
1471 }
1472 }
1473}
1474
1475impl From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
1476 fn from(error: Box<dyn std::error::Error + Send + Sync>) -> ShellError {
1477 ShellError::GenericError {
1478 error: format!("{error:?}"),
1479 msg: error.to_string(),
1480 span: None,
1481 help: None,
1482 inner: vec![],
1483 }
1484 }
1485}
1486
1487impl From<super::LabeledError> for ShellError {
1488 fn from(error: super::LabeledError) -> Self {
1489 ShellError::LabeledError(Box::new(error))
1490 }
1491}
1492
1493impl Serialize for ShellError {
1495 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1496 where
1497 S: serde::Serializer,
1498 {
1499 LabeledError::from_diagnostic(self).serialize(serializer)
1500 }
1501}
1502
1503impl<'de> Deserialize<'de> for ShellError {
1506 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1507 where
1508 D: serde::Deserializer<'de>,
1509 {
1510 LabeledError::deserialize(deserializer).map(ShellError::from)
1511 }
1512}
1513
1514#[test]
1515fn shell_error_serialize_roundtrip() {
1516 let original_error = ShellError::CantConvert {
1519 span: Span::new(100, 200),
1520 to_type: "Foo".into(),
1521 from_type: "Bar".into(),
1522 help: Some("this is a test".into()),
1523 };
1524 println!("orig_error = {original_error:#?}");
1525
1526 let serialized =
1527 serde_json::to_string_pretty(&original_error).expect("serde_json::to_string_pretty failed");
1528 println!("serialized = {serialized}");
1529
1530 let deserialized: ShellError =
1531 serde_json::from_str(&serialized).expect("serde_json::from_str failed");
1532 println!("deserialized = {deserialized:#?}");
1533
1534 assert_eq!(original_error.to_string(), deserialized.to_string());
1537
1538 assert_eq!(
1539 original_error.code().map(|c| c.to_string()),
1540 deserialized.code().map(|c| c.to_string())
1541 );
1542
1543 let orig_labels = original_error
1544 .labels()
1545 .into_iter()
1546 .flatten()
1547 .collect::<Vec<_>>();
1548 let deser_labels = deserialized
1549 .labels()
1550 .into_iter()
1551 .flatten()
1552 .collect::<Vec<_>>();
1553
1554 assert_eq!(orig_labels, deser_labels);
1555
1556 assert_eq!(
1557 original_error.help().map(|c| c.to_string()),
1558 deserialized.help().map(|c| c.to_string())
1559 );
1560}
1561
1562#[cfg(test)]
1563mod test {
1564 use super::*;
1565
1566 impl From<std::io::Error> for ShellError {
1567 fn from(_: std::io::Error) -> ShellError {
1568 unimplemented!(
1569 "This implementation is defined in the test module to ensure no other implementation exists."
1570 )
1571 }
1572 }
1573
1574 impl From<Spanned<std::io::Error>> for ShellError {
1575 fn from(_: Spanned<std::io::Error>) -> Self {
1576 unimplemented!(
1577 "This implementation is defined in the test module to ensure no other implementation exists."
1578 )
1579 }
1580 }
1581
1582 impl From<ShellError> for std::io::Error {
1583 fn from(_: ShellError) -> Self {
1584 unimplemented!(
1585 "This implementation is defined in the test module to ensure no other implementation exists."
1586 )
1587 }
1588 }
1589}