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