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(transparent)]
825 #[diagnostic(transparent)]
826 Network(#[from] network::NetworkError),
827
828 #[error("Command not found")]
834 #[diagnostic(code(nu::shell::command_not_found))]
835 CommandNotFound {
836 #[label("command not found")]
837 span: Span,
838 },
839
840 #[error("Alias not found")]
846 #[diagnostic(code(nu::shell::alias_not_found))]
847 AliasNotFound {
848 #[label("alias not found")]
849 span: Span,
850 },
851
852 #[error("The registered plugin data for `{plugin_name}` is invalid")]
858 #[diagnostic(code(nu::shell::plugin_registry_data_invalid))]
859 PluginRegistryDataInvalid {
860 plugin_name: String,
861 #[label("plugin `{plugin_name}` loaded here")]
862 span: Option<Span>,
863 #[help(
864 "the format in the plugin registry file is not compatible with this version of Nushell.\n\nTry adding the plugin again with `{}`"
865 )]
866 add_command: String,
867 },
868
869 #[error("Plugin failed to load: {msg}")]
875 #[diagnostic(code(nu::shell::plugin_failed_to_load))]
876 PluginFailedToLoad { msg: String },
877
878 #[error("Plugin failed to encode: {msg}")]
884 #[diagnostic(code(nu::shell::plugin_failed_to_encode))]
885 PluginFailedToEncode { msg: String },
886
887 #[error("Plugin failed to decode: {msg}")]
893 #[diagnostic(code(nu::shell::plugin_failed_to_decode))]
894 PluginFailedToDecode { msg: String },
895
896 #[error("Custom value `{name}` cannot be sent to plugin")]
903 #[diagnostic(code(nu::shell::custom_value_incorrect_for_plugin))]
904 CustomValueIncorrectForPlugin {
905 name: String,
906 #[label("the `{dest_plugin}` plugin does not support this kind of value")]
907 span: Span,
908 dest_plugin: String,
909 #[help("this value came from the `{}` plugin")]
910 src_plugin: Option<String>,
911 },
912
913 #[error("Custom value failed to encode")]
920 #[diagnostic(code(nu::shell::custom_value_failed_to_encode))]
921 CustomValueFailedToEncode {
922 msg: String,
923 #[label("{msg}")]
924 span: Span,
925 },
926
927 #[error("Custom value failed to decode")]
934 #[diagnostic(code(nu::shell::custom_value_failed_to_decode))]
935 #[diagnostic(help("the plugin may have been updated and no longer support this custom value"))]
936 CustomValueFailedToDecode {
937 msg: String,
938 #[label("{msg}")]
939 span: Span,
940 },
941
942 #[error(transparent)]
948 #[diagnostic(transparent)]
949 Io(#[from] io::IoError),
950
951 #[error("Name not found")]
957 #[diagnostic(code(nu::shell::name_not_found))]
958 DidYouMean {
959 suggestion: String,
960 #[label("did you mean '{suggestion}'?")]
961 span: Span,
962 },
963
964 #[error("{msg}")]
970 #[diagnostic(code(nu::shell::did_you_mean_custom))]
971 DidYouMeanCustom {
972 msg: String,
973 suggestion: String,
974 #[label("did you mean '{suggestion}'?")]
975 span: Span,
976 },
977
978 #[error("Non-UTF8 string")]
984 #[diagnostic(
985 code(nu::parser::non_utf8),
986 help("see `decode` for handling character sets other than UTF-8")
987 )]
988 NonUtf8 {
989 #[label("non-UTF8 string")]
990 span: Span,
991 },
992
993 #[error("Non-UTF8 string")]
999 #[diagnostic(
1000 code(nu::parser::non_utf8_custom),
1001 help("see `decode` for handling character sets other than UTF-8")
1002 )]
1003 NonUtf8Custom {
1004 msg: String,
1005 #[label("{msg}")]
1006 span: Span,
1007 },
1008
1009 #[error("Encountered {} error(s) when updating config", errors.len())]
1015 #[diagnostic(code(nu::shell::invalid_config))]
1016 InvalidConfig {
1017 #[related]
1018 errors: Vec<ConfigError>,
1019 },
1020
1021 #[error("Value is missing a required '{column}' column")]
1027 #[diagnostic(code(nu::shell::missing_required_column))]
1028 MissingRequiredColumn {
1029 column: &'static str,
1030 #[label("has no '{column}' column")]
1031 span: Span,
1032 },
1033
1034 #[error("Negative value passed when positive one is required")]
1040 #[diagnostic(code(nu::shell::needs_positive_value))]
1041 NeedsPositiveValue {
1042 #[label("use a positive value")]
1043 span: Span,
1044 },
1045
1046 #[error("{error}")]
1048 #[diagnostic(code(nu::shell::error))]
1049 #[deprecated(since = "0.111.1", note = "use `ShellError::Generic` instead")]
1050 GenericError {
1051 error: String,
1052 msg: String,
1053 #[label("{msg}")]
1054 span: Option<Span>,
1055 #[help]
1056 help: Option<String>,
1057 #[related]
1058 inner: Vec<ShellError>,
1059 },
1060
1061 #[error(transparent)]
1063 #[diagnostic(transparent)]
1064 Generic(#[from] generic::GenericError),
1065
1066 #[error("{error}")]
1068 #[diagnostic(code(nu::shell::outsidespan))]
1069 OutsideSpannedLabeledError {
1070 #[source_code]
1071 src: String,
1072 error: String,
1073 msg: String,
1074 #[label("{msg}")]
1075 span: Span,
1076 },
1077
1078 #[error("{msg}")]
1081 #[diagnostic(code(nu::shell::outside), url("{url}"))]
1082 OutsideSource {
1083 #[source_code]
1084 src: NamedSource<String>,
1085 msg: String,
1086 url: String,
1087 #[help]
1088 help: Option<String>,
1089 #[label(collection, "")]
1091 labels: Vec<LabeledSpan>,
1092 #[related]
1093 inner: Vec<ShellError>,
1094 },
1095
1096 #[error("{msg}")]
1099 #[diagnostic(code(nu::shell::outside))]
1100 OutsideSourceNoUrl {
1101 #[source_code]
1102 src: NamedSource<String>,
1103 msg: String,
1104 #[help]
1105 help: Option<String>,
1106 #[label(collection, "")]
1108 labels: Vec<LabeledSpan>,
1109 #[related]
1110 inner: Vec<ShellError>,
1111 },
1112
1113 #[error(transparent)]
1115 #[diagnostic(transparent)]
1116 LabeledError(#[from] Box<super::LabeledError>),
1117
1118 #[error("Removed command: {removed}")]
1124 #[diagnostic(code(nu::shell::removed_command))]
1125 RemovedCommand {
1126 removed: String,
1127 replacement: String,
1128 #[label("'{removed}' has been removed from Nushell. Please use '{replacement}' instead.")]
1129 span: Span,
1130 },
1131
1132 #[error("Eval block failed with pipeline input")]
1135 #[diagnostic(code(nu::shell::eval_block_with_input))]
1136 EvalBlockWithInput {
1137 #[label("source value")]
1138 span: Span,
1139 #[related]
1140 sources: Vec<ShellError>,
1141 },
1142
1143 #[error("Break used outside of loop")]
1145 Break {
1146 #[label("used outside of loop")]
1147 span: Span,
1148 },
1149
1150 #[error("Continue used outside of loop")]
1152 Continue {
1153 #[label("used outside of loop")]
1154 span: Span,
1155 },
1156
1157 #[error("Return used outside of custom command or closure")]
1159 Return {
1160 #[label("used outside of custom command or closure")]
1161 span: Span,
1162 value: Box<Value>,
1163 },
1164
1165 #[error("Exit doesn't catch internally")]
1167 #[diagnostic(
1168 code(nu::shell::exit),
1169 help(
1170 "This shouldn't happen. Please file an issue: https://github.com/nushell/nushell/issues"
1171 )
1172 )]
1173 Exit { code: i32 },
1174
1175 #[error("Recursion limit ({recursion_limit}) reached")]
1181 #[diagnostic(code(nu::shell::recursion_limit_reached))]
1182 RecursionLimitReached {
1183 recursion_limit: u64,
1184 #[label("This called itself too many times")]
1185 span: Option<Span>,
1186 },
1187
1188 #[error("Operation interrupted")]
1190 Interrupted {
1191 #[label("This operation was interrupted")]
1192 span: Span,
1193 },
1194
1195 #[error("Match guard not bool")]
1198 #[diagnostic(
1199 code(nu::shell::match_guard_not_bool),
1200 help("Match guards should evaluate to a boolean")
1201 )]
1202 MatchGuardNotBool {
1203 #[label("not a boolean expression")]
1204 span: Span,
1205 },
1206
1207 #[error("Missing const eval implementation")]
1212 #[diagnostic(
1213 code(nu::shell::missing_const_eval_implementation),
1214 help(
1215 "The command lacks an implementation for constant evaluation. \
1216This is an internal Nushell error, please file an issue https://github.com/nushell/nushell/issues."
1217 )
1218 )]
1219 MissingConstEvalImpl {
1220 #[label("command lacks constant implementation")]
1221 span: Span,
1222 },
1223
1224 #[error("Found parsing error in expression.")]
1232 #[diagnostic(
1233 code(nu::shell::parse_error_in_constant),
1234 help(
1235 "This expression is supposed to be evaluated into a constant, which means error-free."
1236 )
1237 )]
1238 ParseErrorInConstant {
1239 #[label("Parsing error detected in expression")]
1240 span: Span,
1241 },
1242
1243 #[error("Not a constant.")]
1249 #[diagnostic(
1250 code(nu::shell::not_a_constant),
1251 help(
1252 "Only a subset of expressions are allowed constants during parsing. Try using the 'const' command or typing the value literally."
1253 )
1254 )]
1255 NotAConstant {
1256 #[label("Value is not a parse-time constant")]
1257 span: Span,
1258 },
1259
1260 #[error("Not a const command.")]
1267 #[diagnostic(
1268 code(nu::shell::not_a_const_command),
1269 help("Only a subset of builtin commands can run at parse time.")
1270 )]
1271 NotAConstCommand {
1272 #[label("This command cannot run at parse time.")]
1273 span: Span,
1274 },
1275
1276 #[error("Help message not a constant.")]
1282 #[diagnostic(
1283 code(nu::shell::not_a_const_help),
1284 help("Help messages are currently not supported to be constants.")
1285 )]
1286 NotAConstHelp {
1287 #[label("This command cannot run at parse time.")]
1288 span: Span,
1289 },
1290
1291 #[error("{deprecation_type} deprecated.")]
1292 #[diagnostic(code(nu::shell::deprecated), severity(Warning))]
1293 DeprecationWarning {
1294 deprecation_type: &'static str,
1295 suggestion: String,
1296 #[label("{suggestion}")]
1297 span: Span,
1298 #[help]
1299 help: Option<&'static str>,
1300 },
1301
1302 #[error("Invalid glob pattern")]
1308 #[diagnostic(
1309 code(nu::shell::invalid_glob_pattern),
1310 help("Refer to xxx for help on nushell glob patterns.")
1311 )]
1312 InvalidGlobPattern {
1313 msg: String,
1314 #[label("{msg}")]
1315 span: Span,
1316 },
1317
1318 #[error("Invalid unit")]
1324 #[diagnostic(
1325 code(nu::shell::invalid_unit),
1326 help("Supported units are: {supported_units}")
1327 )]
1328 InvalidUnit {
1329 supported_units: String,
1330 #[label("encountered here")]
1331 span: Span,
1332 },
1333
1334 #[error("Not a list")]
1340 #[diagnostic(
1341 code(nu::shell::cannot_spread_as_list),
1342 help(
1343 "Only lists can be spread inside lists and command calls. Try converting the value to a list before spreading."
1344 )
1345 )]
1346 CannotSpreadAsList {
1347 #[label = "cannot spread value"]
1348 span: Span,
1349 },
1350
1351 #[error("Not a record")]
1357 #[diagnostic(
1358 code(nu::shell::cannot_spread_as_record),
1359 help(
1360 "Only records can be spread inside records. Try converting the value to a record before spreading."
1361 )
1362 )]
1363 CannotSpreadAsRecord {
1364 #[label = "cannot spread value"]
1365 span: Span,
1366 },
1367
1368 #[error("Lists are not automatically spread when calling external commands")]
1374 #[diagnostic(
1375 code(nu::shell::cannot_pass_list_to_external),
1376 help("Either convert the list to a string or use the spread operator, like so: ...{arg}")
1377 )]
1378 CannotPassListToExternal {
1379 arg: String,
1380 #[label = "Spread operator (...) is necessary to spread lists"]
1381 span: Span,
1382 },
1383
1384 #[error(
1390 "The selected range {left_flank}..{right_flank} is out of the bounds of the provided input"
1391 )]
1392 #[diagnostic(code(nu::shell::out_of_bounds))]
1393 OutOfBounds {
1394 left_flank: String,
1395 right_flank: String,
1396 #[label = "byte index is not a char boundary or is out of bounds of the input"]
1397 span: Span,
1398 },
1399
1400 #[error("The config directory could not be found")]
1402 #[diagnostic(
1403 code(nu::shell::config_dir_not_found),
1404 help(
1405 r#"On Linux, this would be $XDG_CONFIG_HOME or $HOME/.config.
1406On MacOS, this would be `$HOME/Library/Application Support`.
1407On Windows, this would be %USERPROFILE%\AppData\Roaming"#
1408 )
1409 )]
1410 ConfigDirNotFound {
1411 #[label = "Could not find config directory"]
1412 span: Span,
1413 },
1414
1415 #[error(
1417 "$env.XDG_CONFIG_HOME ({xdg}) is invalid, using default config directory instead: {default}"
1418 )]
1419 #[diagnostic(
1420 code(nu::shell::xdg_config_home_invalid),
1421 help("Set XDG_CONFIG_HOME to an absolute path, or set it to an empty string to ignore it")
1422 )]
1423 InvalidXdgConfig { xdg: String, default: String },
1424
1425 #[error("IR evaluation error: {msg}")]
1432 #[diagnostic(
1433 code(nu::shell::ir_eval_error),
1434 help(
1435 "this is a bug, please report it at https://github.com/nushell/nushell/issues/new along with the code you were running if able"
1436 )
1437 )]
1438 IrEvalError {
1439 msg: String,
1440 #[label = "while running this code"]
1441 span: Option<Span>,
1442 },
1443
1444 #[error("OS feature is disabled: {msg}")]
1445 #[diagnostic(
1446 code(nu::shell::os_disabled),
1447 help("You're probably running outside an OS like a browser, we cannot support this")
1448 )]
1449 DisabledOsSupport {
1450 msg: String,
1451 #[label = "while running this code"]
1452 span: Span,
1453 },
1454
1455 #[error(transparent)]
1456 #[diagnostic(transparent)]
1457 Job(#[from] JobError),
1458
1459 #[error(transparent)]
1460 #[diagnostic(transparent)]
1461 ChainedError(ChainedError),
1462}
1463
1464impl ShellError {
1465 pub fn external_exit_code(&self) -> Option<Spanned<i32>> {
1466 let (item, span) = match *self {
1467 Self::NonZeroExitCode { exit_code, span } => (exit_code.into(), span),
1468 #[cfg(unix)]
1469 Self::TerminatedBySignal { signal, span, .. }
1470 | Self::CoreDumped { signal, span, .. } => (-signal, span),
1471 _ => return None,
1472 };
1473 Some(Spanned { item, span })
1474 }
1475
1476 pub fn exit_code(&self) -> Option<i32> {
1477 match self {
1478 Self::Return { .. } | Self::Break { .. } | Self::Continue { .. } => None,
1479 _ => self.external_exit_code().map(|e| e.item).or(Some(1)),
1480 }
1481 }
1482
1483 pub fn into_full_value(
1484 self,
1485 working_set: &StateWorkingSet,
1486 stack: &Stack,
1487 span: Span,
1488 ) -> Value {
1489 let exit_code = self.external_exit_code();
1490
1491 let mut record = record! {
1492 "msg" => Value::string(self.to_string(), span),
1493 "debug" => Value::string(format!("{self:?}"), span),
1494 "raw" => Value::error(self.clone(), span),
1495 "rendered" => Value::string(format_cli_error(Some(stack), working_set, &self, Some("nu::shell::error")), span),
1496 "json" => Value::string(serde_json::to_string(&self).expect("Could not serialize error"), span),
1497 };
1498
1499 if let Some(code) = exit_code {
1500 record.push("exit_code", Value::int(code.item.into(), code.span));
1501 }
1502
1503 Value::record(record, span)
1504 }
1505
1506 pub fn wrap(self, working_set: &StateWorkingSet, span: Span) -> ParseError {
1508 let msg = format_cli_error(None, working_set, &self, None);
1509 ParseError::LabeledError(
1510 msg,
1511 "Encountered error during parse-time evaluation".into(),
1512 span,
1513 )
1514 }
1515
1516 pub fn into_chained(self, span: Span) -> Self {
1518 Self::ChainedError(match self {
1519 Self::ChainedError(inner) => ChainedError::new_chained(inner, span),
1520 other => {
1521 let error = other.clone();
1524 let mut now = ChainedError::new(other, span);
1525 if let Some(related) = error.related() {
1526 let mapped = related
1527 .map(|s| {
1528 let shellerror: Self = Self::from_diagnostic(s);
1529 shellerror
1530 })
1531 .collect::<Vec<_>>();
1532 if !mapped.is_empty() {
1533 now.sources = [now.sources, mapped].concat();
1534 };
1535 }
1536 now
1537 }
1538 })
1539 }
1540
1541 pub fn from_diagnostic(diag: &(impl miette::Diagnostic + ?Sized)) -> Self {
1542 Self::LabeledError(LabeledError::from_diagnostic(diag).into())
1543 }
1544}
1545
1546impl FromValue for ShellError {
1547 fn from_value(v: Value) -> Result<Self, ShellError> {
1548 let from_type = v.get_type();
1549 match v {
1550 Value::Error { error, .. } => Ok(*error),
1551 Value::Record {
1553 val, internal_span, ..
1554 } => Self::from_value(
1555 (*val)
1556 .get("raw")
1557 .ok_or(ShellError::CantConvert {
1558 to_type: Self::expected_type().to_string(),
1559 from_type: from_type.to_string(),
1560 span: internal_span,
1561 help: None,
1562 })?
1563 .clone(),
1564 ),
1565 Value::Nothing { internal_span } => Ok(Self::Generic(GenericError::new(
1566 "error",
1567 "is nothing",
1568 internal_span,
1569 ))),
1570 _ => Err(ShellError::CantConvert {
1571 to_type: Self::expected_type().to_string(),
1572 from_type: v.get_type().to_string(),
1573 span: v.span(),
1574 help: None,
1575 }),
1576 }
1577 }
1578}
1579
1580impl From<Box<dyn std::error::Error>> for ShellError {
1581 fn from(error: Box<dyn std::error::Error>) -> ShellError {
1582 ShellError::Generic(GenericError::new_internal(
1583 format!("{error:?}"),
1584 error.to_string(),
1585 ))
1586 }
1587}
1588
1589impl From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
1590 fn from(error: Box<dyn std::error::Error + Send + Sync>) -> ShellError {
1591 ShellError::Generic(GenericError::new_internal(
1592 format!("{error:?}"),
1593 error.to_string(),
1594 ))
1595 }
1596}
1597
1598impl From<super::LabeledError> for ShellError {
1599 fn from(error: super::LabeledError) -> Self {
1600 ShellError::LabeledError(Box::new(error))
1601 }
1602}
1603
1604impl Serialize for ShellError {
1606 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1607 where
1608 S: serde::Serializer,
1609 {
1610 LabeledError::from_diagnostic(self).serialize(serializer)
1611 }
1612}
1613
1614impl<'de> Deserialize<'de> for ShellError {
1617 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1618 where
1619 D: serde::Deserializer<'de>,
1620 {
1621 LabeledError::deserialize(deserializer).map(ShellError::from)
1622 }
1623}
1624
1625#[test]
1626fn shell_error_serialize_roundtrip() {
1627 let original_error = ShellError::CantConvert {
1630 span: Span::new(100, 200),
1631 to_type: "Foo".into(),
1632 from_type: "Bar".into(),
1633 help: Some("this is a test".into()),
1634 };
1635 println!("orig_error = {original_error:#?}");
1636
1637 let serialized =
1638 serde_json::to_string_pretty(&original_error).expect("serde_json::to_string_pretty failed");
1639 println!("serialized = {serialized}");
1640
1641 let deserialized: ShellError =
1642 serde_json::from_str(&serialized).expect("serde_json::from_str failed");
1643 println!("deserialized = {deserialized:#?}");
1644
1645 assert_eq!(original_error.to_string(), deserialized.to_string());
1648
1649 assert_eq!(
1650 original_error.code().map(|c| c.to_string()),
1651 deserialized.code().map(|c| c.to_string())
1652 );
1653
1654 let orig_labels = original_error
1655 .labels()
1656 .into_iter()
1657 .flatten()
1658 .collect::<Vec<_>>();
1659 let deser_labels = deserialized
1660 .labels()
1661 .into_iter()
1662 .flatten()
1663 .collect::<Vec<_>>();
1664
1665 assert_eq!(orig_labels, deser_labels);
1666
1667 assert_eq!(
1668 original_error.help().map(|c| c.to_string()),
1669 deserialized.help().map(|c| c.to_string())
1670 );
1671}
1672
1673#[derive(Debug, Clone, Eq, PartialEq)]
1679pub enum ErrorSite {
1680 Span(Span),
1682
1683 Location(String),
1687}
1688
1689impl From<Span> for ErrorSite {
1690 fn from(span: Span) -> Self {
1691 Self::Span(span)
1692 }
1693}
1694
1695impl From<Location> for ErrorSite {
1696 fn from(location: Location) -> Self {
1697 Self::Location(location.to_string())
1698 }
1699}
1700
1701#[derive(Debug, Error, Clone, Diagnostic)]
1703#[error(transparent)]
1704pub struct ErrorSource(Arc<dyn StdError + Send + Sync>);
1705
1706impl PartialEq for ErrorSource {
1707 fn eq(&self, other: &Self) -> bool {
1708 self.0.to_string() == other.0.to_string()
1710 }
1711}
1712
1713#[cfg(test)]
1714mod test {
1715 use super::*;
1716
1717 impl From<std::io::Error> for ShellError {
1718 fn from(_: std::io::Error) -> ShellError {
1719 unimplemented!(
1720 "This implementation is defined in the test module to ensure no other implementation exists."
1721 )
1722 }
1723 }
1724
1725 impl From<Spanned<std::io::Error>> for ShellError {
1726 fn from(_: Spanned<std::io::Error>) -> Self {
1727 unimplemented!(
1728 "This implementation is defined in the test module to ensure no other implementation exists."
1729 )
1730 }
1731 }
1732
1733 impl From<ShellError> for std::io::Error {
1734 fn from(_: ShellError) -> Self {
1735 unimplemented!(
1736 "This implementation is defined in the test module to ensure no other implementation exists."
1737 )
1738 }
1739 }
1740}