1use super::chained_error::ChainedError;
2use crate::{
3 ast::Operator, engine::StateWorkingSet, format_shell_error, record, ConfigError, LabeledError,
4 ParseError, Span, Spanned, Type, Value,
5};
6use miette::Diagnostic;
7use serde::{Deserialize, Serialize};
8use std::num::NonZeroI32;
9use thiserror::Error;
10
11pub mod bridge;
12pub mod io;
13pub mod location;
14
15#[derive(Debug, Clone, Error, Diagnostic, PartialEq)]
19pub enum ShellError {
20 #[error("The '{op}' operator does not work on values of type '{unsupported}'.")]
22 #[diagnostic(code(nu::shell::operator_unsupported_type))]
23 OperatorUnsupportedType {
24 op: Operator,
25 unsupported: Type,
26 #[label = "does not support '{unsupported}'"]
27 op_span: Span,
28 #[label("{unsupported}")]
29 unsupported_span: Span,
30 #[help]
31 help: Option<&'static str>,
32 },
33
34 #[error("Types '{lhs}' and '{rhs}' are not compatible for the '{op}' operator.")]
36 #[diagnostic(code(nu::shell::operator_incompatible_types))]
37 OperatorIncompatibleTypes {
38 op: Operator,
39 lhs: Type,
40 rhs: Type,
41 #[label = "does not operate between '{lhs}' and '{rhs}'"]
42 op_span: Span,
43 #[label("{lhs}")]
44 lhs_span: Span,
45 #[label("{rhs}")]
46 rhs_span: Span,
47 #[help]
48 help: Option<&'static str>,
49 },
50
51 #[error("Operator overflow.")]
58 #[diagnostic(code(nu::shell::operator_overflow))]
59 OperatorOverflow {
60 msg: String,
61 #[label = "{msg}"]
62 span: Span,
63 #[help]
64 help: Option<String>,
65 },
66
67 #[error("Pipeline mismatch.")]
74 #[diagnostic(code(nu::shell::pipeline_mismatch))]
75 PipelineMismatch {
76 exp_input_type: String,
77 #[label("expected: {exp_input_type}")]
78 dst_span: Span,
79 #[label("value originates from here")]
80 src_span: Span,
81 },
82
83 #[error("Input type not supported.")]
93 #[diagnostic(code(nu::shell::only_supports_this_input_type))]
94 OnlySupportsThisInputType {
95 exp_input_type: String,
96 wrong_type: String,
97 #[label("only {exp_input_type} input data is supported")]
98 dst_span: Span,
99 #[label("input type: {wrong_type}")]
100 src_span: Span,
101 },
102
103 #[error("Pipeline empty.")]
109 #[diagnostic(code(nu::shell::pipeline_mismatch))]
110 PipelineEmpty {
111 #[label("no input value was piped in")]
112 dst_span: Span,
113 },
114
115 #[error("Type mismatch.")]
122 #[diagnostic(code(nu::shell::type_mismatch))]
123 TypeMismatch {
124 err_message: String,
125 #[label = "{err_message}"]
126 span: Span,
127 },
128
129 #[error("Type mismatch")]
135 #[diagnostic(code(nu::shell::type_mismatch))]
136 RuntimeTypeMismatch {
137 expected: Type,
138 actual: Type,
139 #[label = "expected {expected}, but got {actual}"]
140 span: Span,
141 },
142
143 #[error("Invalid value")]
149 #[diagnostic(code(nu::shell::invalid_value))]
150 InvalidValue {
151 valid: String,
152 actual: String,
153 #[label = "expected {valid}, but got {actual}"]
154 span: Span,
155 },
156
157 #[error("Incorrect value.")]
163 #[diagnostic(code(nu::shell::incorrect_value))]
164 IncorrectValue {
165 msg: String,
166 #[label = "{msg}"]
167 val_span: Span,
168 #[label = "encountered here"]
169 call_span: Span,
170 },
171
172 #[error("Assignment operations require a variable.")]
178 #[diagnostic(code(nu::shell::assignment_requires_variable))]
179 AssignmentRequiresVar {
180 #[label = "needs to be a variable"]
181 lhs_span: Span,
182 },
183
184 #[error("Assignment to an immutable variable.")]
190 #[diagnostic(code(nu::shell::assignment_requires_mutable_variable))]
191 AssignmentRequiresMutableVar {
192 #[label = "needs to be a mutable variable"]
193 lhs_span: Span,
194 },
195
196 #[error("Unknown operator: {op_token}.")]
202 #[diagnostic(code(nu::shell::unknown_operator))]
203 UnknownOperator {
204 op_token: String,
205 #[label = "unknown operator"]
206 span: Span,
207 },
208
209 #[error("Missing parameter: {param_name}.")]
215 #[diagnostic(code(nu::shell::missing_parameter))]
216 MissingParameter {
217 param_name: String,
218 #[label = "missing parameter: {param_name}"]
219 span: Span,
220 },
221
222 #[error("Incompatible parameters.")]
228 #[diagnostic(code(nu::shell::incompatible_parameters))]
229 IncompatibleParameters {
230 left_message: String,
231 #[label("{left_message}")]
233 left_span: Span,
234 right_message: String,
235 #[label("{right_message}")]
236 right_span: Span,
237 },
238
239 #[error("Delimiter error")]
245 #[diagnostic(code(nu::shell::delimiter_error))]
246 DelimiterError {
247 msg: String,
248 #[label("{msg}")]
249 span: Span,
250 },
251
252 #[error("Incompatible parameters.")]
260 #[diagnostic(code(nu::shell::incompatible_parameters))]
261 IncompatibleParametersSingle {
262 msg: String,
263 #[label = "{msg}"]
264 span: Span,
265 },
266
267 #[error("Running external commands not supported")]
273 #[diagnostic(code(nu::shell::external_commands))]
274 ExternalNotSupported {
275 #[label = "external not supported"]
276 span: Span,
277 },
278
279 #[error("Invalid Probability.")]
286 #[diagnostic(code(nu::shell::invalid_probability))]
287 InvalidProbability {
288 #[label = "invalid probability: must be between 0 and 1"]
289 span: Span,
290 },
291
292 #[error("Invalid range {left_flank}..{right_flank}")]
298 #[diagnostic(code(nu::shell::invalid_range))]
299 InvalidRange {
300 left_flank: String,
301 right_flank: String,
302 #[label = "expected a valid range"]
303 span: Span,
304 },
305
306 #[error("Nushell failed: {msg}.")]
312 #[diagnostic(
313 code(nu::shell::nushell_failed),
314 help(
315 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
316 ))]
317 NushellFailed { msg: String },
319
320 #[error("Nushell failed: {msg}.")]
326 #[diagnostic(
327 code(nu::shell::nushell_failed_spanned),
328 help(
329 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
330 ))]
331 NushellFailedSpanned {
333 msg: String,
334 label: String,
335 #[label = "{label}"]
336 span: Span,
337 },
338
339 #[error("Nushell failed: {msg}.")]
345 #[diagnostic(code(nu::shell::nushell_failed_help))]
346 NushellFailedHelp {
348 msg: String,
349 #[help]
350 help: String,
351 },
352
353 #[error("Variable not found")]
359 #[diagnostic(code(nu::shell::variable_not_found))]
360 VariableNotFoundAtRuntime {
361 #[label = "variable not found"]
362 span: Span,
363 },
364
365 #[error("Environment variable '{envvar_name}' not found")]
371 #[diagnostic(code(nu::shell::env_variable_not_found))]
372 EnvVarNotFoundAtRuntime {
373 envvar_name: String,
374 #[label = "environment variable not found"]
375 span: Span,
376 },
377
378 #[error("Module '{mod_name}' not found")]
384 #[diagnostic(code(nu::shell::module_not_found))]
385 ModuleNotFoundAtRuntime {
386 mod_name: String,
387 #[label = "module not found"]
388 span: Span,
389 },
390
391 #[error("Overlay '{overlay_name}' not found")]
397 #[diagnostic(code(nu::shell::overlay_not_found))]
398 OverlayNotFoundAtRuntime {
399 overlay_name: String,
400 #[label = "overlay not found"]
401 span: Span,
402 },
403
404 #[error("Not found.")]
410 #[diagnostic(code(nu::parser::not_found))]
411 NotFound {
412 #[label = "did not find anything under this name"]
413 span: Span,
414 },
415
416 #[error("Can't convert to {to_type}.")]
422 #[diagnostic(code(nu::shell::cant_convert))]
423 CantConvert {
424 to_type: String,
425 from_type: String,
426 #[label("can't convert {from_type} to {to_type}")]
427 span: Span,
428 #[help]
429 help: Option<String>,
430 },
431
432 #[error("Can't convert string `{details}` to duration.")]
433 #[diagnostic(code(nu::shell::cant_convert_with_value))]
434 CantConvertToDuration {
435 details: String,
436 #[label("can't be converted to duration")]
437 dst_span: Span,
438 #[label("this string value...")]
439 src_span: Span,
440 #[help]
441 help: Option<String>,
442 },
443
444 #[error("'{envvar_name}' is not representable as a string.")]
450 #[diagnostic(
451 code(nu::shell::env_var_not_a_string),
452 help(
453 r#"The '{envvar_name}' environment variable must be a string or be convertible to a string.
454 Either make sure '{envvar_name}' is a string, or add a 'to_string' entry for it in ENV_CONVERSIONS."#
455 )
456 )]
457 EnvVarNotAString {
458 envvar_name: String,
459 #[label("value not representable as a string")]
460 span: Span,
461 },
462
463 #[error("{envvar_name} cannot be set manually.")]
469 #[diagnostic(
470 code(nu::shell::automatic_env_var_set_manually),
471 help(
472 r#"The environment variable '{envvar_name}' is set automatically by Nushell and cannot be set manually."#
473 )
474 )]
475 AutomaticEnvVarSetManually {
476 envvar_name: String,
477 #[label("cannot set '{envvar_name}' manually")]
478 span: Span,
479 },
480
481 #[error("Cannot replace environment.")]
488 #[diagnostic(
489 code(nu::shell::cannot_replace_env),
490 help(r#"Assigning a value to '$env' is not allowed."#)
491 )]
492 CannotReplaceEnv {
493 #[label("setting '$env' not allowed")]
494 span: Span,
495 },
496
497 #[error("Division by zero.")]
503 #[diagnostic(code(nu::shell::division_by_zero))]
504 DivisionByZero {
505 #[label("division by zero")]
506 span: Span,
507 },
508
509 #[error("Can't convert range to countable values")]
517 #[diagnostic(code(nu::shell::range_to_countable))]
518 CannotCreateRange {
519 #[label = "can't convert to countable values"]
520 span: Span,
521 },
522
523 #[error("Row number too large (max: {max_idx}).")]
529 #[diagnostic(code(nu::shell::access_beyond_end))]
530 AccessBeyondEnd {
531 max_idx: usize,
532 #[label = "index too large (max: {max_idx})"]
533 span: Span,
534 },
535
536 #[error("Inserted at wrong row number (should be {available_idx}).")]
542 #[diagnostic(code(nu::shell::access_beyond_end))]
543 InsertAfterNextFreeIndex {
544 available_idx: usize,
545 #[label = "can't insert at index (the next available index is {available_idx})"]
546 span: Span,
547 },
548
549 #[error("Row number too large (empty content).")]
555 #[diagnostic(code(nu::shell::access_beyond_end))]
556 AccessEmptyContent {
557 #[label = "index too large (empty content)"]
558 span: Span,
559 },
560
561 #[error("Row number too large.")]
568 #[diagnostic(code(nu::shell::access_beyond_end_of_stream))]
569 AccessBeyondEndOfStream {
570 #[label = "index too large"]
571 span: Span,
572 },
573
574 #[error("Data cannot be accessed with a cell path")]
580 #[diagnostic(code(nu::shell::incompatible_path_access))]
581 IncompatiblePathAccess {
582 type_name: String,
583 #[label("{type_name} doesn't support cell paths")]
584 span: Span,
585 },
586
587 #[error("Cannot find column '{col_name}'")]
593 #[diagnostic(code(nu::shell::column_not_found))]
594 CantFindColumn {
595 col_name: String,
596 #[label = "cannot find column '{col_name}'"]
597 span: Option<Span>,
598 #[label = "value originates here"]
599 src_span: Span,
600 },
601
602 #[error("Column already exists")]
608 #[diagnostic(code(nu::shell::column_already_exists))]
609 ColumnAlreadyExists {
610 col_name: String,
611 #[label = "column '{col_name}' already exists"]
612 span: Span,
613 #[label = "value originates here"]
614 src_span: Span,
615 },
616
617 #[error("Not a list value")]
623 #[diagnostic(code(nu::shell::not_a_list))]
624 NotAList {
625 #[label = "value not a list"]
626 dst_span: Span,
627 #[label = "value originates here"]
628 src_span: Span,
629 },
630
631 #[error("Record field or table column used twice: {col_name}")]
637 #[diagnostic(code(nu::shell::column_defined_twice))]
638 ColumnDefinedTwice {
639 col_name: String,
640 #[label = "field redefined here"]
641 second_use: Span,
642 #[label = "field first defined here"]
643 first_use: Span,
644 },
645
646 #[error("Attempted to create a record from different number of columns and values")]
652 #[diagnostic(code(nu::shell::record_cols_vals_mismatch))]
653 RecordColsValsMismatch {
654 #[label = "problematic value"]
655 bad_value: Span,
656 #[label = "attempted to create the record here"]
657 creation_site: Span,
658 },
659
660 #[error("Relative range values cannot be used with streams that don't have a known length")]
666 #[diagnostic(code(nu::shell::relative_range_on_infinite_stream))]
667 RelativeRangeOnInfiniteStream {
668 #[label = "Relative range values cannot be used with streams that don't have a known length"]
669 span: Span,
670 },
671
672 #[error("External command failed")]
678 #[diagnostic(code(nu::shell::external_command), help("{help}"))]
679 ExternalCommand {
680 label: String,
681 help: String,
682 #[label("{label}")]
683 span: Span,
684 },
685
686 #[error("External command had a non-zero exit code")]
692 #[diagnostic(code(nu::shell::non_zero_exit_code))]
693 NonZeroExitCode {
694 exit_code: NonZeroI32,
695 #[label("exited with code {exit_code}")]
696 span: Span,
697 },
698
699 #[cfg(unix)]
700 #[error("External command was terminated by a signal")]
706 #[diagnostic(code(nu::shell::terminated_by_signal))]
707 TerminatedBySignal {
708 signal_name: String,
709 signal: i32,
710 #[label("terminated by {signal_name} ({signal})")]
711 span: Span,
712 },
713
714 #[cfg(unix)]
715 #[error("External command core dumped")]
721 #[diagnostic(code(nu::shell::core_dumped))]
722 CoreDumped {
723 signal_name: String,
724 signal: i32,
725 #[label("core dumped with {signal_name} ({signal})")]
726 span: Span,
727 },
728
729 #[error("Unsupported input")]
735 #[diagnostic(code(nu::shell::unsupported_input))]
736 UnsupportedInput {
737 msg: String,
738 input: String,
739 #[label("{msg}")]
740 msg_span: Span,
741 #[label("{input}")]
742 input_span: Span,
743 },
744
745 #[error("Unable to parse datetime: [{msg}].")]
760 #[diagnostic(
761 code(nu::shell::datetime_parse_error),
762 help(
763 r#"Examples of supported inputs:
764 * "5 pm"
765 * "2020/12/4"
766 * "2020.12.04 22:10 +2"
767 * "2020-04-12 22:10:57 +02:00"
768 * "2020-04-12T22:10:57.213231+02:00"
769 * "Tue, 1 Jul 2003 10:52:37 +0200""#
770 )
771 )]
772 DatetimeParseError {
773 msg: String,
774 #[label("datetime parsing failed")]
775 span: Span,
776 },
777
778 #[error("Network failure")]
784 #[diagnostic(code(nu::shell::network_failure))]
785 NetworkFailure {
786 msg: String,
787 #[label("{msg}")]
788 span: Span,
789 },
790
791 #[error("Command not found")]
797 #[diagnostic(code(nu::shell::command_not_found))]
798 CommandNotFound {
799 #[label("command not found")]
800 span: Span,
801 },
802
803 #[error("Alias not found")]
809 #[diagnostic(code(nu::shell::alias_not_found))]
810 AliasNotFound {
811 #[label("alias not found")]
812 span: Span,
813 },
814
815 #[error("The registered plugin data for `{plugin_name}` is invalid")]
821 #[diagnostic(code(nu::shell::plugin_registry_data_invalid))]
822 PluginRegistryDataInvalid {
823 plugin_name: String,
824 #[label("plugin `{plugin_name}` loaded here")]
825 span: Option<Span>,
826 #[help("the format in the plugin registry file is not compatible with this version of Nushell.\n\nTry adding the plugin again with `{}`")]
827 add_command: String,
828 },
829
830 #[error("Plugin failed to load: {msg}")]
836 #[diagnostic(code(nu::shell::plugin_failed_to_load))]
837 PluginFailedToLoad { msg: String },
838
839 #[error("Plugin failed to encode: {msg}")]
845 #[diagnostic(code(nu::shell::plugin_failed_to_encode))]
846 PluginFailedToEncode { msg: String },
847
848 #[error("Plugin failed to decode: {msg}")]
854 #[diagnostic(code(nu::shell::plugin_failed_to_decode))]
855 PluginFailedToDecode { msg: String },
856
857 #[error("Custom value `{name}` cannot be sent to plugin")]
864 #[diagnostic(code(nu::shell::custom_value_incorrect_for_plugin))]
865 CustomValueIncorrectForPlugin {
866 name: String,
867 #[label("the `{dest_plugin}` plugin does not support this kind of value")]
868 span: Span,
869 dest_plugin: String,
870 #[help("this value came from the `{}` plugin")]
871 src_plugin: Option<String>,
872 },
873
874 #[error("Custom value failed to encode")]
881 #[diagnostic(code(nu::shell::custom_value_failed_to_encode))]
882 CustomValueFailedToEncode {
883 msg: String,
884 #[label("{msg}")]
885 span: Span,
886 },
887
888 #[error("Custom value failed to decode")]
895 #[diagnostic(code(nu::shell::custom_value_failed_to_decode))]
896 #[diagnostic(help(
897 "the plugin may have been updated and no longer support this custom value"
898 ))]
899 CustomValueFailedToDecode {
900 msg: String,
901 #[label("{msg}")]
902 span: Span,
903 },
904
905 #[error(transparent)]
911 #[diagnostic(transparent)]
912 Io(io::IoError),
913
914 #[error("Name not found")]
920 #[diagnostic(code(nu::shell::name_not_found))]
921 DidYouMean {
922 suggestion: String,
923 #[label("did you mean '{suggestion}'?")]
924 span: Span,
925 },
926
927 #[error("{msg}")]
933 #[diagnostic(code(nu::shell::did_you_mean_custom))]
934 DidYouMeanCustom {
935 msg: String,
936 suggestion: String,
937 #[label("did you mean '{suggestion}'?")]
938 span: Span,
939 },
940
941 #[error("Non-UTF8 string")]
947 #[diagnostic(
948 code(nu::parser::non_utf8),
949 help("see `decode` for handling character sets other than UTF-8")
950 )]
951 NonUtf8 {
952 #[label("non-UTF8 string")]
953 span: Span,
954 },
955
956 #[error("Non-UTF8 string")]
962 #[diagnostic(
963 code(nu::parser::non_utf8_custom),
964 help("see `decode` for handling character sets other than UTF-8")
965 )]
966 NonUtf8Custom {
967 msg: String,
968 #[label("{msg}")]
969 span: Span,
970 },
971
972 #[error("Encountered {} error(s) when updating config", errors.len())]
978 #[diagnostic(code(nu::shell::invalid_config))]
979 InvalidConfig {
980 #[related]
981 errors: Vec<ConfigError>,
982 },
983
984 #[error("Value is missing a required '{column}' column")]
990 #[diagnostic(code(nu::shell::missing_required_column))]
991 MissingRequiredColumn {
992 column: &'static str,
993 #[label("has no '{column}' column")]
994 span: Span,
995 },
996
997 #[error("Negative value passed when positive one is required")]
1003 #[diagnostic(code(nu::shell::needs_positive_value))]
1004 NeedsPositiveValue {
1005 #[label("use a positive value")]
1006 span: Span,
1007 },
1008
1009 #[error("{error}")]
1011 #[diagnostic()]
1012 GenericError {
1013 error: String,
1014 msg: String,
1015 #[label("{msg}")]
1016 span: Option<Span>,
1017 #[help]
1018 help: Option<String>,
1019 #[related]
1020 inner: Vec<ShellError>,
1021 },
1022
1023 #[error("{error}")]
1025 #[diagnostic()]
1026 OutsideSpannedLabeledError {
1027 #[source_code]
1028 src: String,
1029 error: String,
1030 msg: String,
1031 #[label("{msg}")]
1032 span: Span,
1033 },
1034
1035 #[error(transparent)]
1037 #[diagnostic(transparent)]
1038 LabeledError(#[from] Box<super::LabeledError>),
1039
1040 #[error("Removed command: {removed}")]
1046 #[diagnostic(code(nu::shell::removed_command))]
1047 RemovedCommand {
1048 removed: String,
1049 replacement: String,
1050 #[label("'{removed}' has been removed from Nushell. Please use '{replacement}' instead.")]
1051 span: Span,
1052 },
1053
1054 #[error("Eval block failed with pipeline input")]
1057 #[diagnostic(code(nu::shell::eval_block_with_input))]
1058 EvalBlockWithInput {
1059 #[label("source value")]
1060 span: Span,
1061 #[related]
1062 sources: Vec<ShellError>,
1063 },
1064
1065 #[error("Break used outside of loop")]
1067 Break {
1068 #[label("used outside of loop")]
1069 span: Span,
1070 },
1071
1072 #[error("Continue used outside of loop")]
1074 Continue {
1075 #[label("used outside of loop")]
1076 span: Span,
1077 },
1078
1079 #[error("Return used outside of custom command or closure")]
1081 Return {
1082 #[label("used outside of custom command or closure")]
1083 span: Span,
1084 value: Box<Value>,
1085 },
1086
1087 #[error("Recursion limit ({recursion_limit}) reached")]
1093 #[diagnostic(code(nu::shell::recursion_limit_reached))]
1094 RecursionLimitReached {
1095 recursion_limit: u64,
1096 #[label("This called itself too many times")]
1097 span: Option<Span>,
1098 },
1099
1100 #[error("Operation interrupted")]
1102 Interrupted {
1103 #[label("This operation was interrupted")]
1104 span: Span,
1105 },
1106
1107 #[error("Operation interrupted by user")]
1109 InterruptedByUser {
1110 #[label("This operation was interrupted")]
1111 span: Option<Span>,
1112 },
1113
1114 #[error("Match guard not bool")]
1117 #[diagnostic(
1118 code(nu::shell::match_guard_not_bool),
1119 help("Match guards should evaluate to a boolean")
1120 )]
1121 MatchGuardNotBool {
1122 #[label("not a boolean expression")]
1123 span: Span,
1124 },
1125
1126 #[error("Missing const eval implementation")]
1131 #[diagnostic(
1132 code(nu::shell::missing_const_eval_implementation),
1133 help(
1134 "The command lacks an implementation for constant evaluation. \
1135This is an internal Nushell error, please file an issue https://github.com/nushell/nushell/issues."
1136 )
1137 )]
1138 MissingConstEvalImpl {
1139 #[label("command lacks constant implementation")]
1140 span: Span,
1141 },
1142
1143 #[error("Found parsing error in expression.")]
1151 #[diagnostic(
1152 code(nu::shell::parse_error_in_constant),
1153 help(
1154 "This expression is supposed to be evaluated into a constant, which means error-free."
1155 )
1156 )]
1157 ParseErrorInConstant {
1158 #[label("Parsing error detected in expression")]
1159 span: Span,
1160 },
1161
1162 #[error("Not a constant.")]
1168 #[diagnostic(
1169 code(nu::shell::not_a_constant),
1170 help("Only a subset of expressions are allowed constants during parsing. Try using the 'const' command or typing the value literally.")
1171 )]
1172 NotAConstant {
1173 #[label("Value is not a parse-time constant")]
1174 span: Span,
1175 },
1176
1177 #[error("Not a const command.")]
1184 #[diagnostic(
1185 code(nu::shell::not_a_const_command),
1186 help("Only a subset of builtin commands, and custom commands built only from those commands, can run at parse time.")
1187 )]
1188 NotAConstCommand {
1189 #[label("This command cannot run at parse time.")]
1190 span: Span,
1191 },
1192
1193 #[error("Help message not a constant.")]
1199 #[diagnostic(
1200 code(nu::shell::not_a_const_help),
1201 help("Help messages are currently not supported to be constants.")
1202 )]
1203 NotAConstHelp {
1204 #[label("This command cannot run at parse time.")]
1205 span: Span,
1206 },
1207
1208 #[error("{deprecated} is deprecated and will be removed in a future release")]
1209 #[diagnostic()]
1210 Deprecated {
1211 deprecated: &'static str,
1212 suggestion: &'static str,
1213 #[label("{deprecated} is deprecated. {suggestion}")]
1214 span: Span,
1215 #[help]
1216 help: Option<&'static str>,
1217 },
1218
1219 #[error("Invalid glob pattern")]
1225 #[diagnostic(
1226 code(nu::shell::invalid_glob_pattern),
1227 help("Refer to xxx for help on nushell glob patterns.")
1228 )]
1229 InvalidGlobPattern {
1230 msg: String,
1231 #[label("{msg}")]
1232 span: Span,
1233 },
1234
1235 #[error("Not a list")]
1241 #[diagnostic(
1242 code(nu::shell::cannot_spread_as_list),
1243 help("Only lists can be spread inside lists and command calls. Try converting the value to a list before spreading.")
1244 )]
1245 CannotSpreadAsList {
1246 #[label = "cannot spread value"]
1247 span: Span,
1248 },
1249
1250 #[error("Not a record")]
1256 #[diagnostic(
1257 code(nu::shell::cannot_spread_as_record),
1258 help("Only records can be spread inside records. Try converting the value to a record before spreading.")
1259 )]
1260 CannotSpreadAsRecord {
1261 #[label = "cannot spread value"]
1262 span: Span,
1263 },
1264
1265 #[error("Lists are not automatically spread when calling external commands")]
1271 #[diagnostic(
1272 code(nu::shell::cannot_pass_list_to_external),
1273 help("Either convert the list to a string or use the spread operator, like so: ...{arg}")
1274 )]
1275 CannotPassListToExternal {
1276 arg: String,
1277 #[label = "Spread operator (...) is necessary to spread lists"]
1278 span: Span,
1279 },
1280
1281 #[error(
1287 "The selected range {left_flank}..{right_flank} is out of the bounds of the provided input"
1288 )]
1289 #[diagnostic(code(nu::shell::out_of_bounds))]
1290 OutOfBounds {
1291 left_flank: String,
1292 right_flank: String,
1293 #[label = "byte index is not a char boundary or is out of bounds of the input"]
1294 span: Span,
1295 },
1296
1297 #[error("The config directory could not be found")]
1299 #[diagnostic(
1300 code(nu::shell::config_dir_not_found),
1301 help(
1302 r#"On Linux, this would be $XDG_CONFIG_HOME or $HOME/.config.
1303On MacOS, this would be `$HOME/Library/Application Support`.
1304On Windows, this would be %USERPROFILE%\AppData\Roaming"#
1305 )
1306 )]
1307 ConfigDirNotFound {
1308 #[label = "Could not find config directory"]
1309 span: Option<Span>,
1310 },
1311
1312 #[error("$env.XDG_CONFIG_HOME ({xdg}) is invalid, using default config directory instead: {default}")]
1314 #[diagnostic(
1315 code(nu::shell::xdg_config_home_invalid),
1316 help("Set XDG_CONFIG_HOME to an absolute path, or set it to an empty string to ignore it")
1317 )]
1318 InvalidXdgConfig { xdg: String, default: String },
1319
1320 #[error("IR evaluation error: {msg}")]
1327 #[diagnostic(
1328 code(nu::shell::ir_eval_error),
1329 help("this is a bug, please report it at https://github.com/nushell/nushell/issues/new along with the code you were running if able")
1330 )]
1331 IrEvalError {
1332 msg: String,
1333 #[label = "while running this code"]
1334 span: Option<Span>,
1335 },
1336
1337 #[error("OS feature is disabled: {msg}")]
1338 #[diagnostic(
1339 code(nu::shell::os_disabled),
1340 help("You're probably running outside an OS like a browser, we cannot support this")
1341 )]
1342 DisabledOsSupport {
1343 msg: String,
1344 #[label = "while running this code"]
1345 span: Option<Span>,
1346 },
1347
1348 #[error("Job {id} not found")]
1349 #[diagnostic(
1350 code(nu::shell::job_not_found),
1351 help(
1352 "The operation could not be completed, there is no job currently running with this id"
1353 )
1354 )]
1355 JobNotFound {
1356 id: usize,
1357 #[label = "job not found"]
1358 span: Span,
1359 },
1360
1361 #[error("No frozen job to unfreeze")]
1362 #[diagnostic(
1363 code(nu::shell::no_frozen_job),
1364 help("There is currently no frozen job to unfreeze")
1365 )]
1366 NoFrozenJob {
1367 #[label = "no frozen job"]
1368 span: Span,
1369 },
1370
1371 #[error("Job {id} is not frozen")]
1372 #[diagnostic(
1373 code(nu::shell::job_not_frozen),
1374 help("You tried to unfreeze a job which is not frozen")
1375 )]
1376 JobNotFrozen {
1377 id: usize,
1378 #[label = "job not frozen"]
1379 span: Span,
1380 },
1381
1382 #[error("The job {id} is frozen")]
1383 #[diagnostic(
1384 code(nu::shell::job_is_frozen),
1385 help("This operation cannot be performed because the job is frozen")
1386 )]
1387 JobIsFrozen {
1388 id: usize,
1389 #[label = "This job is frozen"]
1390 span: Span,
1391 },
1392
1393 #[error("No message was received in the requested time interval")]
1394 #[diagnostic(
1395 code(nu::shell::recv_timeout),
1396 help("No message arrived within the specified time limit")
1397 )]
1398 RecvTimeout {
1399 #[label = "timeout"]
1400 span: Span,
1401 },
1402
1403 #[error(transparent)]
1404 #[diagnostic(transparent)]
1405 ChainedError(ChainedError),
1406}
1407
1408impl ShellError {
1409 pub fn external_exit_code(&self) -> Option<Spanned<i32>> {
1410 let (item, span) = match *self {
1411 Self::NonZeroExitCode { exit_code, span } => (exit_code.into(), span),
1412 #[cfg(unix)]
1413 Self::TerminatedBySignal { signal, span, .. }
1414 | Self::CoreDumped { signal, span, .. } => (-signal, span),
1415 _ => return None,
1416 };
1417 Some(Spanned { item, span })
1418 }
1419
1420 pub fn exit_code(&self) -> Option<i32> {
1421 match self {
1422 Self::Return { .. } | Self::Break { .. } | Self::Continue { .. } => None,
1423 _ => self.external_exit_code().map(|e| e.item).or(Some(1)),
1424 }
1425 }
1426
1427 pub fn into_value(self, working_set: &StateWorkingSet, span: Span) -> Value {
1428 let exit_code = self.external_exit_code();
1429
1430 let mut record = record! {
1431 "msg" => Value::string(self.to_string(), span),
1432 "debug" => Value::string(format!("{self:?}"), span),
1433 "raw" => Value::error(self.clone(), span),
1434 "rendered" => Value::string(format_shell_error(working_set, &self), span),
1435 "json" => Value::string(serde_json::to_string(&self).expect("Could not serialize error"), span),
1436 };
1437
1438 if let Some(code) = exit_code {
1439 record.push("exit_code", Value::int(code.item.into(), code.span));
1440 }
1441
1442 Value::record(record, span)
1443 }
1444
1445 pub fn wrap(self, working_set: &StateWorkingSet, span: Span) -> ParseError {
1447 let msg = format_shell_error(working_set, &self);
1448 ParseError::LabeledError(
1449 msg,
1450 "Encountered error during parse-time evaluation".into(),
1451 span,
1452 )
1453 }
1454
1455 pub fn into_chainned(self, span: Span) -> Self {
1457 match self {
1458 ShellError::ChainedError(inner) => {
1459 ShellError::ChainedError(ChainedError::new_chained(inner, span))
1460 }
1461 other => ShellError::ChainedError(ChainedError::new(other, span)),
1462 }
1463 }
1464}
1465
1466impl From<Box<dyn std::error::Error>> for ShellError {
1467 fn from(error: Box<dyn std::error::Error>) -> ShellError {
1468 ShellError::GenericError {
1469 error: format!("{error:?}"),
1470 msg: error.to_string(),
1471 span: None,
1472 help: None,
1473 inner: vec![],
1474 }
1475 }
1476}
1477
1478impl From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
1479 fn from(error: Box<dyn std::error::Error + Send + Sync>) -> ShellError {
1480 ShellError::GenericError {
1481 error: format!("{error:?}"),
1482 msg: error.to_string(),
1483 span: None,
1484 help: None,
1485 inner: vec![],
1486 }
1487 }
1488}
1489
1490impl From<super::LabeledError> for ShellError {
1491 fn from(error: super::LabeledError) -> Self {
1492 ShellError::LabeledError(Box::new(error))
1493 }
1494}
1495
1496impl Serialize for ShellError {
1498 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1499 where
1500 S: serde::Serializer,
1501 {
1502 LabeledError::from_diagnostic(self).serialize(serializer)
1503 }
1504}
1505
1506impl<'de> Deserialize<'de> for ShellError {
1509 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1510 where
1511 D: serde::Deserializer<'de>,
1512 {
1513 LabeledError::deserialize(deserializer).map(ShellError::from)
1514 }
1515}
1516
1517#[test]
1518fn shell_error_serialize_roundtrip() {
1519 let original_error = ShellError::CantConvert {
1522 span: Span::new(100, 200),
1523 to_type: "Foo".into(),
1524 from_type: "Bar".into(),
1525 help: Some("this is a test".into()),
1526 };
1527 println!("orig_error = {:#?}", original_error);
1528
1529 let serialized =
1530 serde_json::to_string_pretty(&original_error).expect("serde_json::to_string_pretty failed");
1531 println!("serialized = {}", serialized);
1532
1533 let deserialized: ShellError =
1534 serde_json::from_str(&serialized).expect("serde_json::from_str failed");
1535 println!("deserialized = {:#?}", deserialized);
1536
1537 assert_eq!(original_error.to_string(), deserialized.to_string());
1540
1541 assert_eq!(
1542 original_error.code().map(|c| c.to_string()),
1543 deserialized.code().map(|c| c.to_string())
1544 );
1545
1546 let orig_labels = original_error
1547 .labels()
1548 .into_iter()
1549 .flatten()
1550 .collect::<Vec<_>>();
1551 let deser_labels = deserialized
1552 .labels()
1553 .into_iter()
1554 .flatten()
1555 .collect::<Vec<_>>();
1556
1557 assert_eq!(orig_labels, deser_labels);
1558
1559 assert_eq!(
1560 original_error.help().map(|c| c.to_string()),
1561 deserialized.help().map(|c| c.to_string())
1562 );
1563}
1564
1565#[cfg(test)]
1566mod test {
1567 use super::*;
1568
1569 impl From<std::io::Error> for ShellError {
1570 fn from(_: std::io::Error) -> ShellError {
1571 unimplemented!("This implementation is defined in the test module to ensure no other implementation exists.")
1572 }
1573 }
1574
1575 impl From<Spanned<std::io::Error>> for ShellError {
1576 fn from(_: Spanned<std::io::Error>) -> Self {
1577 unimplemented!("This implementation is defined in the test module to ensure no other implementation exists.")
1578 }
1579 }
1580
1581 impl From<ShellError> for std::io::Error {
1582 fn from(_: ShellError) -> Self {
1583 unimplemented!("This implementation is defined in the test module to ensure no other implementation exists.")
1584 }
1585 }
1586}