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("Not a constant.")]
1149 #[diagnostic(
1150 code(nu::shell::not_a_constant),
1151 help("Only a subset of expressions are allowed constants during parsing. Try using the 'const' command or typing the value literally.")
1152 )]
1153 NotAConstant {
1154 #[label("Value is not a parse-time constant")]
1155 span: Span,
1156 },
1157
1158 #[error("Not a const command.")]
1165 #[diagnostic(
1166 code(nu::shell::not_a_const_command),
1167 help("Only a subset of builtin commands, and custom commands built only from those commands, can run at parse time.")
1168 )]
1169 NotAConstCommand {
1170 #[label("This command cannot run at parse time.")]
1171 span: Span,
1172 },
1173
1174 #[error("Help message not a constant.")]
1180 #[diagnostic(
1181 code(nu::shell::not_a_const_help),
1182 help("Help messages are currently not supported to be constants.")
1183 )]
1184 NotAConstHelp {
1185 #[label("This command cannot run at parse time.")]
1186 span: Span,
1187 },
1188
1189 #[error("{deprecated} is deprecated and will be removed in a future release")]
1190 #[diagnostic()]
1191 Deprecated {
1192 deprecated: &'static str,
1193 suggestion: &'static str,
1194 #[label("{deprecated} is deprecated. {suggestion}")]
1195 span: Span,
1196 #[help]
1197 help: Option<&'static str>,
1198 },
1199
1200 #[error("Invalid glob pattern")]
1206 #[diagnostic(
1207 code(nu::shell::invalid_glob_pattern),
1208 help("Refer to xxx for help on nushell glob patterns.")
1209 )]
1210 InvalidGlobPattern {
1211 msg: String,
1212 #[label("{msg}")]
1213 span: Span,
1214 },
1215
1216 #[error("Not a list")]
1222 #[diagnostic(
1223 code(nu::shell::cannot_spread_as_list),
1224 help("Only lists can be spread inside lists and command calls. Try converting the value to a list before spreading.")
1225 )]
1226 CannotSpreadAsList {
1227 #[label = "cannot spread value"]
1228 span: Span,
1229 },
1230
1231 #[error("Not a record")]
1237 #[diagnostic(
1238 code(nu::shell::cannot_spread_as_record),
1239 help("Only records can be spread inside records. Try converting the value to a record before spreading.")
1240 )]
1241 CannotSpreadAsRecord {
1242 #[label = "cannot spread value"]
1243 span: Span,
1244 },
1245
1246 #[error("Lists are not automatically spread when calling external commands")]
1252 #[diagnostic(
1253 code(nu::shell::cannot_pass_list_to_external),
1254 help("Either convert the list to a string or use the spread operator, like so: ...{arg}")
1255 )]
1256 CannotPassListToExternal {
1257 arg: String,
1258 #[label = "Spread operator (...) is necessary to spread lists"]
1259 span: Span,
1260 },
1261
1262 #[error(
1268 "The selected range {left_flank}..{right_flank} is out of the bounds of the provided input"
1269 )]
1270 #[diagnostic(code(nu::shell::out_of_bounds))]
1271 OutOfBounds {
1272 left_flank: String,
1273 right_flank: String,
1274 #[label = "byte index is not a char boundary or is out of bounds of the input"]
1275 span: Span,
1276 },
1277
1278 #[error("The config directory could not be found")]
1280 #[diagnostic(
1281 code(nu::shell::config_dir_not_found),
1282 help(
1283 r#"On Linux, this would be $XDG_CONFIG_HOME or $HOME/.config.
1284On MacOS, this would be `$HOME/Library/Application Support`.
1285On Windows, this would be %USERPROFILE%\AppData\Roaming"#
1286 )
1287 )]
1288 ConfigDirNotFound {
1289 #[label = "Could not find config directory"]
1290 span: Option<Span>,
1291 },
1292
1293 #[error("$env.XDG_CONFIG_HOME ({xdg}) is invalid, using default config directory instead: {default}")]
1295 #[diagnostic(
1296 code(nu::shell::xdg_config_home_invalid),
1297 help("Set XDG_CONFIG_HOME to an absolute path, or set it to an empty string to ignore it")
1298 )]
1299 InvalidXdgConfig { xdg: String, default: String },
1300
1301 #[error("IR evaluation error: {msg}")]
1308 #[diagnostic(
1309 code(nu::shell::ir_eval_error),
1310 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")
1311 )]
1312 IrEvalError {
1313 msg: String,
1314 #[label = "while running this code"]
1315 span: Option<Span>,
1316 },
1317
1318 #[error("OS feature is disabled: {msg}")]
1319 #[diagnostic(
1320 code(nu::shell::os_disabled),
1321 help("You're probably running outside an OS like a browser, we cannot support this")
1322 )]
1323 DisabledOsSupport {
1324 msg: String,
1325 #[label = "while running this code"]
1326 span: Option<Span>,
1327 },
1328
1329 #[error("Job {id} not found")]
1330 #[diagnostic(
1331 code(nu::shell::job_not_found),
1332 help(
1333 "The operation could not be completed, there is no job currently running with this id"
1334 )
1335 )]
1336 JobNotFound {
1337 id: usize,
1338 #[label = "job not found"]
1339 span: Span,
1340 },
1341
1342 #[error("No frozen job to unfreeze")]
1343 #[diagnostic(
1344 code(nu::shell::no_frozen_job),
1345 help("There is currently no frozen job to unfreeze")
1346 )]
1347 NoFrozenJob {
1348 #[label = "no frozen job"]
1349 span: Span,
1350 },
1351
1352 #[error("Job {id} is not frozen")]
1353 #[diagnostic(
1354 code(nu::shell::os_disabled),
1355 help("You tried to unfreeze a job which is not frozen")
1356 )]
1357 JobNotFrozen {
1358 id: usize,
1359 #[label = "job not frozen"]
1360 span: Span,
1361 },
1362
1363 #[error(transparent)]
1364 #[diagnostic(transparent)]
1365 ChainedError(ChainedError),
1366}
1367
1368impl ShellError {
1369 pub fn external_exit_code(&self) -> Option<Spanned<i32>> {
1370 let (item, span) = match *self {
1371 Self::NonZeroExitCode { exit_code, span } => (exit_code.into(), span),
1372 #[cfg(unix)]
1373 Self::TerminatedBySignal { signal, span, .. }
1374 | Self::CoreDumped { signal, span, .. } => (-signal, span),
1375 _ => return None,
1376 };
1377 Some(Spanned { item, span })
1378 }
1379
1380 pub fn exit_code(&self) -> Option<i32> {
1381 match self {
1382 Self::Return { .. } | Self::Break { .. } | Self::Continue { .. } => None,
1383 _ => self.external_exit_code().map(|e| e.item).or(Some(1)),
1384 }
1385 }
1386
1387 pub fn into_value(self, working_set: &StateWorkingSet, span: Span) -> Value {
1388 let exit_code = self.external_exit_code();
1389
1390 let mut record = record! {
1391 "msg" => Value::string(self.to_string(), span),
1392 "debug" => Value::string(format!("{self:?}"), span),
1393 "raw" => Value::error(self.clone(), span),
1394 "rendered" => Value::string(format_shell_error(working_set, &self), span),
1395 "json" => Value::string(serde_json::to_string(&self).expect("Could not serialize error"), span),
1396 };
1397
1398 if let Some(code) = exit_code {
1399 record.push("exit_code", Value::int(code.item.into(), code.span));
1400 }
1401
1402 Value::record(record, span)
1403 }
1404
1405 pub fn wrap(self, working_set: &StateWorkingSet, span: Span) -> ParseError {
1407 let msg = format_shell_error(working_set, &self);
1408 ParseError::LabeledError(
1409 msg,
1410 "Encountered error during parse-time evaluation".into(),
1411 span,
1412 )
1413 }
1414
1415 pub fn into_chainned(self, span: Span) -> Self {
1417 match self {
1418 ShellError::ChainedError(inner) => {
1419 ShellError::ChainedError(ChainedError::new_chained(inner, span))
1420 }
1421 other => ShellError::ChainedError(ChainedError::new(other, span)),
1422 }
1423 }
1424}
1425
1426impl From<Box<dyn std::error::Error>> for ShellError {
1427 fn from(error: Box<dyn std::error::Error>) -> ShellError {
1428 ShellError::GenericError {
1429 error: format!("{error:?}"),
1430 msg: error.to_string(),
1431 span: None,
1432 help: None,
1433 inner: vec![],
1434 }
1435 }
1436}
1437
1438impl From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
1439 fn from(error: Box<dyn std::error::Error + Send + Sync>) -> ShellError {
1440 ShellError::GenericError {
1441 error: format!("{error:?}"),
1442 msg: error.to_string(),
1443 span: None,
1444 help: None,
1445 inner: vec![],
1446 }
1447 }
1448}
1449
1450impl From<super::LabeledError> for ShellError {
1451 fn from(error: super::LabeledError) -> Self {
1452 ShellError::LabeledError(Box::new(error))
1453 }
1454}
1455
1456impl Serialize for ShellError {
1458 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1459 where
1460 S: serde::Serializer,
1461 {
1462 LabeledError::from_diagnostic(self).serialize(serializer)
1463 }
1464}
1465
1466impl<'de> Deserialize<'de> for ShellError {
1469 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1470 where
1471 D: serde::Deserializer<'de>,
1472 {
1473 LabeledError::deserialize(deserializer).map(ShellError::from)
1474 }
1475}
1476
1477#[test]
1478fn shell_error_serialize_roundtrip() {
1479 let original_error = ShellError::CantConvert {
1482 span: Span::new(100, 200),
1483 to_type: "Foo".into(),
1484 from_type: "Bar".into(),
1485 help: Some("this is a test".into()),
1486 };
1487 println!("orig_error = {:#?}", original_error);
1488
1489 let serialized =
1490 serde_json::to_string_pretty(&original_error).expect("serde_json::to_string_pretty failed");
1491 println!("serialized = {}", serialized);
1492
1493 let deserialized: ShellError =
1494 serde_json::from_str(&serialized).expect("serde_json::from_str failed");
1495 println!("deserialized = {:#?}", deserialized);
1496
1497 assert_eq!(original_error.to_string(), deserialized.to_string());
1500
1501 assert_eq!(
1502 original_error.code().map(|c| c.to_string()),
1503 deserialized.code().map(|c| c.to_string())
1504 );
1505
1506 let orig_labels = original_error
1507 .labels()
1508 .into_iter()
1509 .flatten()
1510 .collect::<Vec<_>>();
1511 let deser_labels = deserialized
1512 .labels()
1513 .into_iter()
1514 .flatten()
1515 .collect::<Vec<_>>();
1516
1517 assert_eq!(orig_labels, deser_labels);
1518
1519 assert_eq!(
1520 original_error.help().map(|c| c.to_string()),
1521 deserialized.help().map(|c| c.to_string())
1522 );
1523}
1524
1525#[cfg(test)]
1526mod test {
1527 use super::*;
1528
1529 impl From<std::io::Error> for ShellError {
1530 fn from(_: std::io::Error) -> ShellError {
1531 unimplemented!("This implementation is defined in the test module to ensure no other implementation exists.")
1532 }
1533 }
1534
1535 impl From<Spanned<std::io::Error>> for ShellError {
1536 fn from(_: Spanned<std::io::Error>) -> Self {
1537 unimplemented!("This implementation is defined in the test module to ensure no other implementation exists.")
1538 }
1539 }
1540
1541 impl From<ShellError> for std::io::Error {
1542 fn from(_: ShellError) -> Self {
1543 unimplemented!("This implementation is defined in the test module to ensure no other implementation exists.")
1544 }
1545 }
1546}