1#![allow(unused_assignments)]
2use super::chained_error::ChainedError;
3use crate::{
4 ConfigError, FromValue, LabeledError, ParseError, Span, Spanned, Type, Value,
5 ast::Operator,
6 engine::{Stack, StateWorkingSet},
7 format_cli_error, record,
8};
9use generic::GenericError;
10use job::JobError;
11use miette::{Diagnostic, LabeledSpan, NamedSource};
12use nu_utils::location::Location;
13use serde::{Deserialize, Serialize};
14use std::{error::Error as StdError, num::NonZeroI32, sync::Arc};
15use thiserror::Error;
16
17pub mod bridge;
18pub mod generic;
19pub mod io;
20pub mod job;
21pub mod network;
22
23#[derive(Debug, Clone, Error, Diagnostic, PartialEq)]
27pub enum ShellError {
28 #[error("The '{op}' operator does not work on values of type '{unsupported}'.")]
30 #[diagnostic(code(nu::shell::operator_unsupported_type))]
31 OperatorUnsupportedType {
32 op: Operator,
33 unsupported: Type,
34 #[label = "does not support '{unsupported}'"]
35 op_span: Span,
36 #[label("{unsupported}")]
37 unsupported_span: Span,
38 #[help]
39 help: Option<&'static str>,
40 },
41
42 #[error("Types '{lhs}' and '{rhs}' are not compatible for the '{op}' operator.")]
44 #[diagnostic(code(nu::shell::operator_incompatible_types))]
45 OperatorIncompatibleTypes {
46 op: Operator,
47 lhs: Type,
48 rhs: Type,
49 #[label = "does not operate between '{lhs}' and '{rhs}'"]
50 op_span: Span,
51 #[label("{lhs}")]
52 lhs_span: Span,
53 #[label("{rhs}")]
54 rhs_span: Span,
55 #[help]
56 help: Option<&'static str>,
57 },
58
59 #[error("Operator overflow.")]
66 #[diagnostic(code(nu::shell::operator_overflow))]
67 OperatorOverflow {
68 msg: String,
69 #[label = "{msg}"]
70 span: Span,
71 #[help]
72 help: Option<String>,
73 },
74
75 #[error("Pipeline mismatch.")]
82 #[diagnostic(code(nu::shell::pipeline_mismatch))]
83 PipelineMismatch {
84 exp_input_type: String,
85 #[label("expected: {exp_input_type}")]
86 dst_span: Span,
87 #[label("value originates here")]
88 src_span: Span,
89 },
90
91 #[error("Input type not supported.")]
101 #[diagnostic(code(nu::shell::only_supports_this_input_type))]
102 OnlySupportsThisInputType {
103 exp_input_type: String,
104 wrong_type: String,
105 #[label("only {exp_input_type} input data is supported")]
106 dst_span: Span,
107 #[label("input type: {wrong_type}")]
108 src_span: Span,
109 },
110
111 #[error("Pipeline empty.")]
117 #[diagnostic(code(nu::shell::pipeline_mismatch))]
118 PipelineEmpty {
119 #[label("no input value was piped in")]
120 dst_span: Span,
121 },
122
123 #[error("Type mismatch.")]
130 #[diagnostic(code(nu::shell::type_mismatch))]
131 TypeMismatch {
132 err_message: String,
133 #[label = "{err_message}"]
134 span: Span,
135 },
136
137 #[error("Type mismatch")]
143 #[diagnostic(code(nu::shell::type_mismatch))]
144 RuntimeTypeMismatch {
145 expected: Type,
146 actual: Type,
147 #[label = "expected {expected}, but got {actual}"]
148 span: Span,
149 },
150
151 #[error("Invalid value")]
157 #[diagnostic(code(nu::shell::invalid_value))]
158 InvalidValue {
159 valid: String,
160 actual: String,
161 #[label = "expected {valid}, but got {actual}"]
162 span: Span,
163 },
164
165 #[error("Incorrect value.")]
171 #[diagnostic(code(nu::shell::incorrect_value))]
172 IncorrectValue {
173 msg: String,
174 #[label = "{msg}"]
175 val_span: Span,
176 #[label = "encountered here"]
177 call_span: Span,
178 },
179
180 #[error("Assignment operations require a variable.")]
186 #[diagnostic(code(nu::shell::assignment_requires_variable))]
187 AssignmentRequiresVar {
188 #[label = "needs to be a variable"]
189 lhs_span: Span,
190 },
191
192 #[error("Assignment to an immutable variable.")]
198 #[diagnostic(code(nu::shell::assignment_requires_mutable_variable))]
199 AssignmentRequiresMutableVar {
200 #[label = "needs to be a mutable variable"]
201 lhs_span: Span,
202 },
203
204 #[error("Unknown operator: {op_token}.")]
210 #[diagnostic(code(nu::shell::unknown_operator))]
211 UnknownOperator {
212 op_token: String,
213 #[label = "unknown operator"]
214 span: Span,
215 },
216
217 #[error("Missing parameter: {param_name}.")]
223 #[diagnostic(code(nu::shell::missing_parameter))]
224 MissingParameter {
225 param_name: String,
226 #[label = "missing parameter: {param_name}"]
227 span: Span,
228 },
229
230 #[error("Incompatible parameters.")]
236 #[diagnostic(code(nu::shell::incompatible_parameters))]
237 IncompatibleParameters {
238 left_message: String,
239 #[label("{left_message}")]
241 left_span: Span,
242 right_message: String,
243 #[label("{right_message}")]
244 right_span: Span,
245 },
246
247 #[error("Delimiter error")]
253 #[diagnostic(code(nu::shell::delimiter_error))]
254 DelimiterError {
255 msg: String,
256 #[label("{msg}")]
257 span: Span,
258 },
259
260 #[error("Incompatible parameters.")]
268 #[diagnostic(code(nu::shell::incompatible_parameters))]
269 IncompatibleParametersSingle {
270 msg: String,
271 #[label = "{msg}"]
272 span: Span,
273 },
274
275 #[error("Running external commands not supported")]
281 #[diagnostic(code(nu::shell::external_commands))]
282 ExternalNotSupported {
283 #[label = "external not supported"]
284 span: Span,
285 },
286
287 #[error("Invalid Probability.")]
294 #[diagnostic(code(nu::shell::invalid_probability))]
295 InvalidProbability {
296 #[label = "invalid probability: must be between 0 and 1"]
297 span: Span,
298 },
299
300 #[error("Invalid range {left_flank}..{right_flank}")]
306 #[diagnostic(code(nu::shell::invalid_range))]
307 InvalidRange {
308 left_flank: String,
309 right_flank: String,
310 #[label = "expected a valid range"]
311 span: Span,
312 },
313
314 #[error("Nushell failed: {msg}.")]
320 #[diagnostic(
321 code(nu::shell::nushell_failed),
322 help(
323 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
324 )
325 )]
326 NushellFailed { msg: String },
328
329 #[error("Nushell failed: {msg}.")]
335 #[diagnostic(
336 code(nu::shell::nushell_failed_spanned),
337 help(
338 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
339 )
340 )]
341 NushellFailedSpanned {
343 msg: String,
344 label: String,
345 #[label = "{label}"]
346 span: Span,
347 },
348
349 #[error("Nushell failed: {msg}.")]
355 #[diagnostic(code(nu::shell::nushell_failed_help))]
356 NushellFailedHelp {
358 msg: String,
359 #[help]
360 help: String,
361 },
362
363 #[error("Variable not found")]
369 #[diagnostic(code(nu::shell::variable_not_found))]
370 VariableNotFoundAtRuntime {
371 #[label = "variable not found"]
372 span: Span,
373 },
374
375 #[error("Environment variable '{envvar_name}' not found")]
381 #[diagnostic(code(nu::shell::env_variable_not_found))]
382 EnvVarNotFoundAtRuntime {
383 envvar_name: String,
384 #[label = "environment variable not found"]
385 span: Span,
386 },
387
388 #[error("Module '{mod_name}' not found")]
394 #[diagnostic(code(nu::shell::module_not_found))]
395 ModuleNotFoundAtRuntime {
396 mod_name: String,
397 #[label = "module not found"]
398 span: Span,
399 },
400
401 #[error("Overlay '{overlay_name}' not found")]
407 #[diagnostic(code(nu::shell::overlay_not_found))]
408 OverlayNotFoundAtRuntime {
409 overlay_name: String,
410 #[label = "overlay not found"]
411 span: Span,
412 },
413
414 #[error("Not found.")]
420 #[diagnostic(code(nu::parser::not_found))]
421 NotFound {
422 #[label = "did not find anything under this name"]
423 span: Span,
424 },
425
426 #[error("Can't convert to {to_type}.")]
432 #[diagnostic(code(nu::shell::cant_convert))]
433 CantConvert {
434 to_type: String,
435 from_type: String,
436 #[label("can't convert {from_type} to {to_type}")]
437 span: Span,
438 #[help]
439 help: Option<String>,
440 },
441
442 #[error("Can't convert {from_type} to the specified unit.")]
448 #[diagnostic(code(nu::shell::cant_convert_value_to_unit))]
449 CantConvertToUnit {
450 to_type: String,
451 from_type: String,
452 #[label("can't convert {from_type} to {to_type}")]
453 span: Span,
454 #[label("conversion originates here")]
455 unit_span: Span,
456 #[help]
457 help: Option<String>,
458 },
459
460 #[error("'{envvar_name}' is not representable as a string.")]
466 #[diagnostic(
467 code(nu::shell::env_var_not_a_string),
468 help(
469 "The '{envvar_name}' environment variable must be a string or be convertible to a string.
470 Either make sure '{envvar_name}' is a string, or add a 'to_string' entry for it in ENV_CONVERSIONS."
471 )
472 )]
473 EnvVarNotAString {
474 envvar_name: String,
475 #[label("value not representable as a string")]
476 span: Span,
477 },
478
479 #[error("{envvar_name} cannot be set manually.")]
485 #[diagnostic(
486 code(nu::shell::automatic_env_var_set_manually),
487 help(
488 "The environment variable '{envvar_name}' is set automatically by Nushell and cannot be set manually."
489 )
490 )]
491 AutomaticEnvVarSetManually {
492 envvar_name: String,
493 #[label("cannot set '{envvar_name}' manually")]
494 span: Span,
495 },
496
497 #[error("Cannot replace environment.")]
504 #[diagnostic(
505 code(nu::shell::cannot_replace_env),
506 help("Assigning a value to '$env' is not allowed.")
507 )]
508 CannotReplaceEnv {
509 #[label("setting '$env' not allowed")]
510 span: Span,
511 },
512
513 #[error("Division by zero.")]
519 #[diagnostic(code(nu::shell::division_by_zero))]
520 DivisionByZero {
521 #[label("division by zero")]
522 span: Span,
523 },
524
525 #[error("Can't convert range to countable values")]
533 #[diagnostic(code(nu::shell::range_to_countable))]
534 CannotCreateRange {
535 #[label = "can't convert to countable values"]
536 span: Span,
537 },
538
539 #[error("Row number too large (max: {max_idx}).")]
545 #[diagnostic(code(nu::shell::access_beyond_end))]
546 AccessBeyondEnd {
547 max_idx: usize,
548 #[label = "index too large (max: {max_idx})"]
549 span: Span,
550 },
551
552 #[error("Inserted at wrong row number (should be {available_idx}).")]
558 #[diagnostic(code(nu::shell::access_beyond_end))]
559 InsertAfterNextFreeIndex {
560 available_idx: usize,
561 #[label = "can't insert at index (the next available index is {available_idx})"]
562 span: Span,
563 },
564
565 #[error("Row number too large (empty content).")]
571 #[diagnostic(code(nu::shell::access_beyond_end))]
572 AccessEmptyContent {
573 #[label = "index too large (empty content)"]
574 span: Span,
575 },
576
577 #[error("Row number too large.")]
584 #[diagnostic(code(nu::shell::access_beyond_end_of_stream))]
585 AccessBeyondEndOfStream {
586 #[label = "index too large"]
587 span: Span,
588 },
589
590 #[error("Data cannot be accessed with a cell path")]
596 #[diagnostic(code(nu::shell::incompatible_path_access))]
597 IncompatiblePathAccess {
598 type_name: String,
599 #[label("{type_name} doesn't support cell paths")]
600 span: Span,
601 },
602
603 #[error("Cannot find column '{col_name}'")]
609 #[diagnostic(
610 code(nu::shell::column_not_found),
611 help = "If some rows have this column, try using '{col_name}?' for optional access, or pre-fill using the `default` command"
612 )]
613 CantFindColumn {
614 col_name: String,
615 #[label = "column '{col_name}' is missing in one or more values"]
616 span: Option<Span>,
617 #[label = "value originates here"]
618 src_span: Span,
619 },
620
621 #[error("Column already exists")]
627 #[diagnostic(code(nu::shell::column_already_exists))]
628 ColumnAlreadyExists {
629 col_name: String,
630 #[label = "column '{col_name}' already exists"]
631 span: Span,
632 #[label = "value originates here"]
633 src_span: Span,
634 },
635
636 #[error("Not a list value")]
642 #[diagnostic(code(nu::shell::not_a_list))]
643 NotAList {
644 #[label = "value not a list"]
645 dst_span: Span,
646 #[label = "value originates here"]
647 src_span: Span,
648 },
649
650 #[error("Record field or table column used twice: {col_name}")]
656 #[diagnostic(code(nu::shell::column_defined_twice))]
657 ColumnDefinedTwice {
658 col_name: String,
659 #[label = "field redefined here"]
660 second_use: Span,
661 #[label = "field first defined here"]
662 first_use: Span,
663 },
664
665 #[error("Attempted to create a record from different number of columns and values")]
671 #[diagnostic(code(nu::shell::record_cols_vals_mismatch))]
672 RecordColsValsMismatch {
673 #[label = "problematic value"]
674 bad_value: Span,
675 #[label = "attempted to create the record here"]
676 creation_site: Span,
677 },
678
679 #[error("Failed to detect columns")]
685 #[diagnostic(code(nu::shell::failed_to_detect_columns))]
686 ColumnDetectionFailure {
687 #[label = "value coming from here"]
688 bad_value: Span,
689 #[label = "tried to detect columns here"]
690 failure_site: Span,
691 },
692
693 #[error("Relative range values cannot be used with streams that don't have a known length")]
699 #[diagnostic(code(nu::shell::relative_range_on_infinite_stream))]
700 RelativeRangeOnInfiniteStream {
701 #[label = "Relative range values cannot be used with streams that don't have a known length"]
702 span: Span,
703 },
704
705 #[error("External command failed")]
711 #[diagnostic(code(nu::shell::external_command), help("{help}"))]
712 ExternalCommand {
713 label: String,
714 help: String,
715 #[label("{label}")]
716 span: Span,
717 },
718
719 #[error("External command had a non-zero exit code")]
725 #[diagnostic(code(nu::shell::non_zero_exit_code))]
726 NonZeroExitCode {
727 exit_code: NonZeroI32,
728 #[label("exited with code {exit_code}")]
729 span: Span,
730 },
731
732 #[cfg(unix)]
733 #[error("External command was terminated by a signal")]
739 #[diagnostic(code(nu::shell::terminated_by_signal))]
740 TerminatedBySignal {
741 signal_name: String,
742 signal: i32,
743 #[label("terminated by {signal_name} ({signal})")]
744 span: Span,
745 },
746
747 #[cfg(unix)]
748 #[error("External command core dumped")]
754 #[diagnostic(code(nu::shell::core_dumped))]
755 CoreDumped {
756 signal_name: String,
757 signal: i32,
758 #[label("core dumped with {signal_name} ({signal})")]
759 span: Span,
760 },
761
762 #[error("Unsupported input")]
768 #[diagnostic(code(nu::shell::unsupported_input))]
769 UnsupportedInput {
770 msg: String,
771 input: String,
772 #[label("{msg}")]
773 msg_span: Span,
774 #[label("{input}")]
775 input_span: Span,
776 },
777
778 #[error("Unable to parse datetime: [{msg}].")]
793 #[diagnostic(
794 code(nu::shell::datetime_parse_error),
795 help(
796 r#"Examples of supported inputs:
797 * "5 pm"
798 * "2020/12/4"
799 * "2020.12.04 22:10 +2"
800 * "2020-04-12 22:10:57 +02:00"
801 * "2020-04-12T22:10:57.213231+02:00"
802 * "Tue, 1 Jul 2003 10:52:37 +0200""#
803 )
804 )]
805 DatetimeParseError {
806 msg: String,
807 #[label("datetime parsing failed")]
808 span: Span,
809 },
810
811 #[error("Network failure")]
817 #[diagnostic(code(nu::shell::network_failure))]
818 NetworkFailure {
819 msg: String,
820 #[label("{msg}")]
821 span: Span,
822 },
823
824 #[error("HTTP Error {code} ({reason}): {url}")]
830 #[diagnostic(code(nu::shell::http_error))]
831 HttpError {
832 code: u16,
833 reason: &'static str,
834 url: String,
835 msg: String,
836 #[label("{msg}")]
837 span: Span,
838 },
839
840 #[error(transparent)]
841 #[diagnostic(transparent)]
842 Network(#[from] network::NetworkError),
843
844 #[error("Command not found")]
850 #[diagnostic(code(nu::shell::command_not_found))]
851 CommandNotFound {
852 #[label("command not found")]
853 span: Span,
854 },
855
856 #[error("Alias not found")]
862 #[diagnostic(code(nu::shell::alias_not_found))]
863 AliasNotFound {
864 #[label("alias not found")]
865 span: Span,
866 },
867
868 #[error("The registered plugin data for `{plugin_name}` is invalid")]
874 #[diagnostic(code(nu::shell::plugin_registry_data_invalid))]
875 PluginRegistryDataInvalid {
876 plugin_name: String,
877 #[label("plugin `{plugin_name}` loaded here")]
878 span: Option<Span>,
879 #[help(
880 "the format in the plugin registry file is not compatible with this version of Nushell.\n\nTry adding the plugin again with `{}`"
881 )]
882 add_command: String,
883 },
884
885 #[error("Plugin failed to load: {msg}")]
891 #[diagnostic(code(nu::shell::plugin_failed_to_load))]
892 PluginFailedToLoad { msg: String },
893
894 #[error("Plugin failed to encode: {msg}")]
900 #[diagnostic(code(nu::shell::plugin_failed_to_encode))]
901 PluginFailedToEncode { msg: String },
902
903 #[error("Plugin failed to decode: {msg}")]
909 #[diagnostic(code(nu::shell::plugin_failed_to_decode))]
910 PluginFailedToDecode { msg: String },
911
912 #[error("Custom value `{name}` cannot be sent to plugin")]
919 #[diagnostic(code(nu::shell::custom_value_incorrect_for_plugin))]
920 CustomValueIncorrectForPlugin {
921 name: String,
922 #[label("the `{dest_plugin}` plugin does not support this kind of value")]
923 span: Span,
924 dest_plugin: String,
925 #[help("this value came from the `{}` plugin")]
926 src_plugin: Option<String>,
927 },
928
929 #[error("Custom value failed to encode")]
936 #[diagnostic(code(nu::shell::custom_value_failed_to_encode))]
937 CustomValueFailedToEncode {
938 msg: String,
939 #[label("{msg}")]
940 span: Span,
941 },
942
943 #[error("Custom value failed to decode")]
950 #[diagnostic(code(nu::shell::custom_value_failed_to_decode))]
951 #[diagnostic(help("the plugin may have been updated and no longer support this custom value"))]
952 CustomValueFailedToDecode {
953 msg: String,
954 #[label("{msg}")]
955 span: Span,
956 },
957
958 #[error(transparent)]
964 #[diagnostic(transparent)]
965 Io(#[from] io::IoError),
966
967 #[error("Name not found")]
973 #[diagnostic(code(nu::shell::name_not_found))]
974 DidYouMean {
975 suggestion: String,
976 #[label("did you mean '{suggestion}'?")]
977 span: Span,
978 },
979
980 #[error("{msg}")]
986 #[diagnostic(code(nu::shell::did_you_mean_custom))]
987 DidYouMeanCustom {
988 msg: String,
989 suggestion: String,
990 #[label("did you mean '{suggestion}'?")]
991 span: Span,
992 },
993
994 #[error("Non-UTF8 string")]
1000 #[diagnostic(
1001 code(nu::parser::non_utf8),
1002 help("see `decode` for handling character sets other than UTF-8")
1003 )]
1004 NonUtf8 {
1005 #[label("non-UTF8 string")]
1006 span: Span,
1007 },
1008
1009 #[error("Non-UTF8 string")]
1015 #[diagnostic(
1016 code(nu::parser::non_utf8_custom),
1017 help("see `decode` for handling character sets other than UTF-8")
1018 )]
1019 NonUtf8Custom {
1020 msg: String,
1021 #[label("{msg}")]
1022 span: Span,
1023 },
1024
1025 #[error("Encountered {} error(s) when updating config", errors.len())]
1031 #[diagnostic(code(nu::shell::invalid_config))]
1032 InvalidConfig {
1033 #[related]
1034 errors: Vec<ConfigError>,
1035 },
1036
1037 #[error("Value is missing a required '{column}' column")]
1043 #[diagnostic(code(nu::shell::missing_required_column))]
1044 MissingRequiredColumn {
1045 column: &'static str,
1046 #[label("has no '{column}' column")]
1047 span: Span,
1048 },
1049
1050 #[error("Negative value passed when positive one is required")]
1056 #[diagnostic(code(nu::shell::needs_positive_value))]
1057 NeedsPositiveValue {
1058 #[label("use a positive value")]
1059 span: Span,
1060 },
1061
1062 #[error("{error}")]
1064 #[diagnostic(code(nu::shell::error))]
1065 #[deprecated(since = "0.111.1", note = "use `ShellError::Generic` instead")]
1066 GenericError {
1067 error: String,
1068 msg: String,
1069 #[label("{msg}")]
1070 span: Option<Span>,
1071 #[help]
1072 help: Option<String>,
1073 #[related]
1074 inner: Vec<ShellError>,
1075 },
1076
1077 #[error(transparent)]
1079 #[diagnostic(transparent)]
1080 Generic(#[from] generic::GenericError),
1081
1082 #[error("{error}")]
1084 #[diagnostic(code(nu::shell::outsidespan))]
1085 OutsideSpannedLabeledError {
1086 #[source_code]
1087 src: String,
1088 error: String,
1089 msg: String,
1090 #[label("{msg}")]
1091 span: Span,
1092 },
1093
1094 #[error("{msg}")]
1097 #[diagnostic(code(nu::shell::outside), url("{url}"))]
1098 OutsideSource {
1099 #[source_code]
1100 src: NamedSource<String>,
1101 msg: String,
1102 url: String,
1103 #[help]
1104 help: Option<String>,
1105 #[label(collection, "")]
1107 labels: Vec<LabeledSpan>,
1108 #[related]
1109 inner: Vec<ShellError>,
1110 },
1111
1112 #[error("{msg}")]
1115 #[diagnostic(code(nu::shell::outside))]
1116 OutsideSourceNoUrl {
1117 #[source_code]
1118 src: NamedSource<String>,
1119 msg: String,
1120 #[help]
1121 help: Option<String>,
1122 #[label(collection, "")]
1124 labels: Vec<LabeledSpan>,
1125 #[related]
1126 inner: Vec<ShellError>,
1127 },
1128
1129 #[error(transparent)]
1131 #[diagnostic(transparent)]
1132 LabeledError(#[from] Box<super::LabeledError>),
1133
1134 #[error("Removed command: {removed}")]
1140 #[diagnostic(code(nu::shell::removed_command))]
1141 RemovedCommand {
1142 removed: String,
1143 replacement: String,
1144 #[label("'{removed}' has been removed from Nushell. Please use '{replacement}' instead.")]
1145 span: Span,
1146 },
1147
1148 #[error("Eval block failed with pipeline input")]
1151 #[diagnostic(code(nu::shell::eval_block_with_input))]
1152 EvalBlockWithInput {
1153 #[label("source value")]
1154 span: Span,
1155 #[related]
1156 sources: Vec<ShellError>,
1157 },
1158
1159 #[error("Break used outside of loop")]
1161 Break {
1162 #[label("used outside of loop")]
1163 span: Span,
1164 },
1165
1166 #[error("Continue used outside of loop")]
1168 Continue {
1169 #[label("used outside of loop")]
1170 span: Span,
1171 },
1172
1173 #[error("Return used outside of custom command or closure")]
1175 Return {
1176 #[label("used outside of custom command or closure")]
1177 span: Span,
1178 value: Box<Value>,
1179 },
1180
1181 #[error("Exit doesn't catch internally")]
1183 #[diagnostic(
1184 code(nu::shell::exit),
1185 help(
1186 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
1187 )
1188 )]
1189 Exit { code: i32, abort: bool },
1190
1191 #[error("Recursion limit ({recursion_limit}) reached")]
1197 #[diagnostic(code(nu::shell::recursion_limit_reached))]
1198 RecursionLimitReached {
1199 recursion_limit: u64,
1200 #[label("This called itself too many times")]
1201 span: Option<Span>,
1202 },
1203
1204 #[error("Operation interrupted")]
1206 Interrupted {
1207 #[label("This operation was interrupted")]
1208 span: Span,
1209 },
1210
1211 #[error("Match guard not bool")]
1214 #[diagnostic(
1215 code(nu::shell::match_guard_not_bool),
1216 help("Match guards should evaluate to a boolean")
1217 )]
1218 MatchGuardNotBool {
1219 #[label("not a boolean expression")]
1220 span: Span,
1221 },
1222
1223 #[error("Missing const eval implementation")]
1228 #[diagnostic(
1229 code(nu::shell::missing_const_eval_implementation),
1230 help(
1231 "The command lacks an implementation for constant evaluation. \
1232This is an internal Nushell error, please file an issue https://github.com/nushell/nushell/issues."
1233 )
1234 )]
1235 MissingConstEvalImpl {
1236 #[label("command lacks constant implementation")]
1237 span: Span,
1238 },
1239
1240 #[error("Found parsing error in expression.")]
1248 #[diagnostic(
1249 code(nu::shell::parse_error_in_constant),
1250 help(
1251 "This expression is supposed to be evaluated into a constant, which means error-free."
1252 )
1253 )]
1254 ParseErrorInConstant {
1255 #[label("Parsing error detected in expression")]
1256 span: Span,
1257 },
1258
1259 #[error("Not a constant.")]
1265 #[diagnostic(
1266 code(nu::shell::not_a_constant),
1267 help(
1268 "Only a subset of expressions are allowed constants during parsing. Try using the 'const' command or typing the value literally."
1269 )
1270 )]
1271 NotAConstant {
1272 #[label("Value is not a parse-time constant")]
1273 span: Span,
1274 },
1275
1276 #[error("Not a const command.")]
1283 #[diagnostic(
1284 code(nu::shell::not_a_const_command),
1285 help("Only a subset of builtin commands can run at parse time.")
1286 )]
1287 NotAConstCommand {
1288 #[label("This command cannot run at parse time.")]
1289 span: Span,
1290 },
1291
1292 #[error("Help message not a constant.")]
1298 #[diagnostic(
1299 code(nu::shell::not_a_const_help),
1300 help("Help messages are currently not supported to be constants.")
1301 )]
1302 NotAConstHelp {
1303 #[label("This command cannot run at parse time.")]
1304 span: Span,
1305 },
1306
1307 #[error("{deprecation_type} deprecated.")]
1308 #[diagnostic(code(nu::shell::deprecated), severity(Warning))]
1309 DeprecationWarning {
1310 deprecation_type: &'static str,
1311 suggestion: String,
1312 #[label("{suggestion}")]
1313 span: Span,
1314 #[help]
1315 help: Option<&'static str>,
1316 },
1317
1318 #[error("Invalid glob pattern")]
1324 #[diagnostic(
1325 code(nu::shell::invalid_glob_pattern),
1326 help("Refer to xxx for help on nushell glob patterns.")
1327 )]
1328 InvalidGlobPattern {
1329 msg: String,
1330 #[label("{msg}")]
1331 span: Span,
1332 },
1333
1334 #[error("Invalid unit")]
1340 #[diagnostic(
1341 code(nu::shell::invalid_unit),
1342 help("Supported units are: {supported_units}")
1343 )]
1344 InvalidUnit {
1345 supported_units: String,
1346 #[label("encountered here")]
1347 span: Span,
1348 },
1349
1350 #[error("Not a list")]
1356 #[diagnostic(
1357 code(nu::shell::cannot_spread_as_list),
1358 help(
1359 "Only lists can be spread inside lists and command calls. Try converting the value to a list before spreading."
1360 )
1361 )]
1362 CannotSpreadAsList {
1363 #[label = "cannot spread value"]
1364 span: Span,
1365 },
1366
1367 #[error("Not a record")]
1373 #[diagnostic(
1374 code(nu::shell::cannot_spread_as_record),
1375 help(
1376 "Only records can be spread inside records. Try converting the value to a record before spreading."
1377 )
1378 )]
1379 CannotSpreadAsRecord {
1380 #[label = "cannot spread value"]
1381 span: Span,
1382 },
1383
1384 #[error("Lists are not automatically spread when calling external commands")]
1390 #[diagnostic(
1391 code(nu::shell::cannot_pass_list_to_external),
1392 help("Either convert the list to a string or use the spread operator, like so: ...{arg}")
1393 )]
1394 CannotPassListToExternal {
1395 arg: String,
1396 #[label = "Spread operator (...) is necessary to spread lists"]
1397 span: Span,
1398 },
1399
1400 #[error(
1406 "The selected range {left_flank}..{right_flank} is out of the bounds of the provided input"
1407 )]
1408 #[diagnostic(code(nu::shell::out_of_bounds))]
1409 OutOfBounds {
1410 left_flank: String,
1411 right_flank: String,
1412 #[label = "byte index is not a char boundary or is out of bounds of the input"]
1413 span: Span,
1414 },
1415
1416 #[error("The config directory could not be found")]
1418 #[diagnostic(
1419 code(nu::shell::config_dir_not_found),
1420 help(
1421 r#"On Linux, this would be $XDG_CONFIG_HOME or $HOME/.config.
1422On MacOS, this would be `$HOME/Library/Application Support`.
1423On Windows, this would be %USERPROFILE%\AppData\Roaming"#
1424 )
1425 )]
1426 ConfigDirNotFound {
1427 #[label = "Could not find config directory"]
1428 span: Span,
1429 },
1430
1431 #[error(
1433 "$env.XDG_CONFIG_HOME ({xdg}) is invalid, using default config directory instead: {default}"
1434 )]
1435 #[diagnostic(
1436 code(nu::shell::xdg_config_home_invalid),
1437 help("Set XDG_CONFIG_HOME to an absolute path, or set it to an empty string to ignore it")
1438 )]
1439 InvalidXdgConfig { xdg: String, default: String },
1440
1441 #[error("IR evaluation error: {msg}")]
1448 #[diagnostic(
1449 code(nu::shell::ir_eval_error),
1450 help(
1451 "this is a bug, please report it at https://github.com/nushell/nushell/issues/new along with the code you were running if able"
1452 )
1453 )]
1454 IrEvalError {
1455 msg: String,
1456 #[label = "while running this code"]
1457 span: Option<Span>,
1458 },
1459
1460 #[error("OS feature is disabled: {msg}")]
1461 #[diagnostic(
1462 code(nu::shell::os_disabled),
1463 help("You're probably running outside an OS like a browser, we cannot support this")
1464 )]
1465 DisabledOsSupport {
1466 msg: String,
1467 #[label = "while running this code"]
1468 span: Span,
1469 },
1470
1471 #[error(transparent)]
1472 #[diagnostic(transparent)]
1473 Job(#[from] JobError),
1474
1475 #[error(transparent)]
1476 #[diagnostic(transparent)]
1477 ChainedError(ChainedError),
1478}
1479
1480impl ShellError {
1481 pub fn external_exit_code(&self) -> Option<Spanned<i32>> {
1482 let (item, span) = match *self {
1483 Self::NonZeroExitCode { exit_code, span } => (exit_code.into(), span),
1484 #[cfg(unix)]
1485 Self::TerminatedBySignal { signal, span, .. }
1486 | Self::CoreDumped { signal, span, .. } => (-signal, span),
1487 _ => return None,
1488 };
1489 Some(Spanned { item, span })
1490 }
1491
1492 pub fn exit_code(&self) -> Option<i32> {
1493 match self {
1494 Self::Return { .. } | Self::Break { .. } | Self::Continue { .. } => None,
1495 _ => self.external_exit_code().map(|e| e.item).or(Some(1)),
1496 }
1497 }
1498
1499 pub fn into_full_value(
1500 self,
1501 working_set: &StateWorkingSet,
1502 stack: &Stack,
1503 span: Span,
1504 ) -> Value {
1505 let exit_code = self.external_exit_code();
1506
1507 let mut record = record! {
1508 "msg" => Value::string(self.to_string(), span),
1509 "debug" => Value::string(format!("{self:?}"), span),
1510 "raw" => Value::error(self.clone(), span),
1511 "rendered" => Value::string(format_cli_error(Some(stack), working_set, &self, Some("nu::shell::error")), span),
1512 "details" => LabeledError::from(self).into_value(span, working_set),
1513 };
1514
1515 if let Some(code) = exit_code {
1516 record.push("exit_code", Value::int(code.item.into(), code.span));
1517 }
1518
1519 Value::record(record, span)
1520 }
1521
1522 pub fn wrap(self, working_set: &StateWorkingSet, span: Span) -> ParseError {
1524 let msg = format_cli_error(None, working_set, &self, None);
1525 ParseError::LabeledError(
1526 msg,
1527 "Encountered error during parse-time evaluation".into(),
1528 span,
1529 )
1530 }
1531
1532 pub fn into_chained(self, span: Span) -> Self {
1534 Self::ChainedError(match self {
1535 Self::ChainedError(inner) => ChainedError::new_chained(inner, span),
1536 other => {
1537 let error = other.clone();
1540 let mut now = ChainedError::new(other, span);
1541 if let Some(related) = error.related() {
1542 let mapped = related
1543 .map(|s| {
1544 let shellerror: Self = Self::from_diagnostic(s);
1545 shellerror
1546 })
1547 .collect::<Vec<_>>();
1548 if !mapped.is_empty() {
1549 now.sources = [now.sources, mapped].concat();
1550 };
1551 }
1552 now
1553 }
1554 })
1555 }
1556
1557 pub fn from_diagnostic(diag: &(impl miette::Diagnostic + ?Sized)) -> Self {
1558 Self::LabeledError(LabeledError::from_diagnostic(diag).into())
1559 }
1560}
1561
1562impl FromValue for ShellError {
1563 fn from_value(v: Value) -> Result<Self, ShellError> {
1564 let from_type = v.get_type();
1565 match v {
1566 Value::Error { error, .. } => Ok(*error),
1567 Value::Record {
1569 val, internal_span, ..
1570 } => Self::from_value(
1571 (*val)
1572 .get("raw")
1573 .ok_or(ShellError::CantConvert {
1574 to_type: Self::expected_type().to_string(),
1575 from_type: from_type.to_string(),
1576 span: internal_span,
1577 help: None,
1578 })?
1579 .clone(),
1580 ),
1581 Value::Nothing { internal_span } => Ok(Self::Generic(GenericError::new(
1582 "error",
1583 "is nothing",
1584 internal_span,
1585 ))),
1586 _ => Err(ShellError::CantConvert {
1587 to_type: Self::expected_type().to_string(),
1588 from_type: v.get_type().to_string(),
1589 span: v.span(),
1590 help: None,
1591 }),
1592 }
1593 }
1594}
1595
1596impl From<Box<dyn std::error::Error>> for ShellError {
1597 fn from(error: Box<dyn std::error::Error>) -> ShellError {
1598 ShellError::Generic(GenericError::new_internal(
1599 format!("{error:?}"),
1600 error.to_string(),
1601 ))
1602 }
1603}
1604
1605impl From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
1606 fn from(error: Box<dyn std::error::Error + Send + Sync>) -> ShellError {
1607 ShellError::Generic(GenericError::new_internal(
1608 format!("{error:?}"),
1609 error.to_string(),
1610 ))
1611 }
1612}
1613
1614impl From<super::LabeledError> for ShellError {
1615 fn from(error: super::LabeledError) -> Self {
1616 ShellError::LabeledError(Box::new(error))
1617 }
1618}
1619
1620impl Serialize for ShellError {
1622 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1623 where
1624 S: serde::Serializer,
1625 {
1626 LabeledError::from_diagnostic(self).serialize(serializer)
1627 }
1628}
1629
1630impl<'de> Deserialize<'de> for ShellError {
1633 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1634 where
1635 D: serde::Deserializer<'de>,
1636 {
1637 LabeledError::deserialize(deserializer).map(ShellError::from)
1638 }
1639}
1640
1641#[test]
1642fn shell_error_serialize_roundtrip() {
1643 let original_error = ShellError::CantConvert {
1646 span: Span::new(100, 200),
1647 to_type: "Foo".into(),
1648 from_type: "Bar".into(),
1649 help: Some("this is a test".into()),
1650 };
1651 println!("orig_error = {original_error:#?}");
1652
1653 let serialized =
1654 serde_json::to_string_pretty(&original_error).expect("serde_json::to_string_pretty failed");
1655 println!("serialized = {serialized}");
1656
1657 let deserialized: ShellError =
1658 serde_json::from_str(&serialized).expect("serde_json::from_str failed");
1659 println!("deserialized = {deserialized:#?}");
1660
1661 assert_eq!(original_error.to_string(), deserialized.to_string());
1664
1665 assert_eq!(
1666 original_error.code().map(|c| c.to_string()),
1667 deserialized.code().map(|c| c.to_string())
1668 );
1669
1670 let orig_labels = original_error
1671 .labels()
1672 .into_iter()
1673 .flatten()
1674 .collect::<Vec<_>>();
1675 let deser_labels = deserialized
1676 .labels()
1677 .into_iter()
1678 .flatten()
1679 .collect::<Vec<_>>();
1680
1681 assert_eq!(orig_labels, deser_labels);
1682
1683 assert_eq!(
1684 original_error.help().map(|c| c.to_string()),
1685 deserialized.help().map(|c| c.to_string())
1686 );
1687}
1688
1689#[derive(Debug, Clone, Eq, PartialEq)]
1695pub enum ErrorSite {
1696 Span(Span),
1698
1699 Location(String),
1703}
1704
1705impl From<Span> for ErrorSite {
1706 fn from(span: Span) -> Self {
1707 Self::Span(span)
1708 }
1709}
1710
1711impl From<Location> for ErrorSite {
1712 fn from(location: Location) -> Self {
1713 Self::Location(location.to_string())
1714 }
1715}
1716
1717#[derive(Debug, Error, Clone, Diagnostic)]
1719#[error(transparent)]
1720pub struct ErrorSource(Arc<dyn StdError + Send + Sync>);
1721
1722impl PartialEq for ErrorSource {
1723 fn eq(&self, other: &Self) -> bool {
1724 self.0.to_string() == other.0.to_string()
1726 }
1727}
1728
1729#[cfg(test)]
1730mod test {
1731 use super::*;
1732
1733 impl From<std::io::Error> for ShellError {
1734 fn from(_: std::io::Error) -> ShellError {
1735 unimplemented!(
1736 "This implementation is defined in the test module to ensure no other implementation exists."
1737 )
1738 }
1739 }
1740
1741 impl From<Spanned<std::io::Error>> for ShellError {
1742 fn from(_: Spanned<std::io::Error>) -> Self {
1743 unimplemented!(
1744 "This implementation is defined in the test module to ensure no other implementation exists."
1745 )
1746 }
1747 }
1748
1749 impl From<ShellError> for std::io::Error {
1750 fn from(_: ShellError) -> Self {
1751 unimplemented!(
1752 "This implementation is defined in the test module to ensure no other implementation exists."
1753 )
1754 }
1755 }
1756}