Skip to main content

llama_cpp_bindings/
error.rs

1use std::ffi::NulError;
2use std::num::NonZeroI32;
3use std::os::raw::c_int;
4use std::path::PathBuf;
5use std::string::FromUtf8Error;
6
7use crate::batch_add_error::BatchAddError;
8use crate::mtmd::MtmdEvalError;
9use crate::mtmd::mtmd_input_chunk_type::MtmdInputChunkTypeError;
10
11/// A failable result from a llama.cpp function.
12pub type Result<TValue> = std::result::Result<TValue, LlamaCppError>;
13
14/// All errors that can occur in the llama-cpp crate.
15#[derive(Debug, Eq, PartialEq, thiserror::Error)]
16pub enum LlamaCppError {
17    /// The backend was already initialized. This can generally be ignored as initializing the backend
18    /// is idempotent.
19    #[error("BackendAlreadyInitialized")]
20    BackendAlreadyInitialized,
21    /// There was an error while get the chat template from model.
22    #[error("{0}")]
23    ChatTemplateError(#[from] ChatTemplateError),
24    /// There was an error while decoding a batch.
25    #[error("{0}")]
26    DecodeError(#[from] DecodeError),
27    /// There was an error while encoding a batch.
28    #[error("{0}")]
29    EncodeError(#[from] EncodeError),
30    /// There was an error loading a model.
31    #[error("{0}")]
32    LlamaModelLoadError(#[from] LlamaModelLoadError),
33    /// There was an error creating a new model context.
34    #[error("{0}")]
35    LlamaContextLoadError(#[from] LlamaContextLoadError),
36    /// There was an error adding a token to a batch.
37    #[error["{0}"]]
38    BatchAddError(#[from] BatchAddError),
39    /// see [`EmbeddingsError`]
40    #[error(transparent)]
41    EmbeddingError(#[from] EmbeddingsError),
42    /// Backend device not found
43    #[error("Backend device {0} not found")]
44    BackendDeviceNotFound(usize),
45    /// Max devices exceeded
46    #[error("Max devices exceeded. Max devices is {0}")]
47    MaxDevicesExceeded(usize),
48    /// Failed to convert JSON schema to grammar.
49    #[error("JsonSchemaToGrammarError: {0}")]
50    JsonSchemaToGrammarError(String),
51    /// see [`FitError`]
52    #[error(transparent)]
53    FitError(#[from] FitError),
54}
55
56/// There was an error while getting the chat template from a model.
57#[derive(Debug, Eq, PartialEq, thiserror::Error)]
58pub enum ChatTemplateError {
59    /// gguf has no chat template (by that name)
60    #[error("chat template not found - returned null pointer")]
61    MissingTemplate,
62
63    /// chat template contained a null byte
64    #[error("null byte in string {0}")]
65    NullError(#[from] NulError),
66
67    /// The chat template was not valid utf8.
68    #[error(transparent)]
69    Utf8Error(#[from] std::str::Utf8Error),
70}
71
72/// Failed fetching metadata value
73#[derive(Debug, Eq, PartialEq, thiserror::Error)]
74pub enum MetaValError {
75    /// The provided string contains an unexpected null-byte
76    #[error("null byte in string {0}")]
77    NullError(#[from] NulError),
78
79    /// The returned data contains invalid UTF8 data
80    #[error("FromUtf8Error {0}")]
81    FromUtf8Error(#[from] FromUtf8Error),
82
83    /// Got negative return value. This happens if the key or index queried does not exist.
84    #[error("Negative return value. Likely due to a missing index or key. Got return value: {0}")]
85    NegativeReturn(i32),
86}
87
88/// Failed to Load context
89#[derive(Debug, Eq, PartialEq, thiserror::Error)]
90pub enum LlamaContextLoadError {
91    /// llama.cpp returned null
92    #[error("null reference from llama.cpp")]
93    NullReturn,
94}
95
96/// Failed to decode a batch.
97#[derive(Debug, Eq, PartialEq, thiserror::Error)]
98pub enum DecodeError {
99    /// No kv cache slot was available.
100    #[error("Decode Error 1: NoKvCacheSlot")]
101    NoKvCacheSlot,
102    /// The computation was aborted by the abort callback.
103    #[error("Decode Error 2: Aborted")]
104    Aborted,
105    /// The number of tokens in the batch was 0.
106    #[error("Decode Error -1: n_tokens == 0")]
107    NTokensZero,
108    /// An unknown error occurred.
109    #[error("Decode Error {0}: unknown")]
110    Unknown(c_int),
111}
112
113/// Failed to decode a batch.
114#[derive(Debug, Eq, PartialEq, thiserror::Error)]
115pub enum EncodeError {
116    /// No kv cache slot was available.
117    #[error("Encode Error 1: NoKvCacheSlot")]
118    NoKvCacheSlot,
119    /// The number of tokens in the batch was 0.
120    #[error("Encode Error -1: n_tokens == 0")]
121    NTokensZero,
122    /// An unknown error occurred.
123    #[error("Encode Error {0}: unknown")]
124    Unknown(c_int),
125}
126
127/// When embedding related functions fail
128#[derive(Debug, Eq, PartialEq, thiserror::Error)]
129pub enum EmbeddingsError {
130    /// Embeddings weren't enabled in the context options
131    #[error("Embeddings weren't enabled in the context options")]
132    NotEnabled,
133    /// Logits weren't enabled for the given token
134    #[error("Logits were not enabled for the given token")]
135    LogitsNotEnabled,
136    /// The given sequence index exceeds the max sequence id
137    #[error("Can't use sequence embeddings with a model supporting only LLAMA_POOLING_TYPE_NONE")]
138    NonePoolType,
139    /// The embedding dimension does not fit into a usize.
140    #[error("Invalid embedding dimension: {0}")]
141    InvalidEmbeddingDimension(#[source] std::num::TryFromIntError),
142}
143
144/// When logits-related functions fail
145#[derive(Debug, Eq, PartialEq, thiserror::Error)]
146pub enum LogitsError {
147    /// The logits data pointer is null.
148    #[error("logits data pointer is null")]
149    NullLogits,
150    /// The requested token index has not been initialized for logits.
151    #[error("logit for token index {0} is not initialized")]
152    TokenNotInitialized(i32),
153    /// The token index exceeds the context size.
154    #[error("token index {token_index} exceeds context size {context_size}")]
155    TokenIndexExceedsContext {
156        /// The token index that was requested.
157        token_index: u32,
158        /// The context size.
159        context_size: u32,
160    },
161    /// The vocabulary size does not fit into a usize.
162    #[error("n_vocab does not fit into usize: {0}")]
163    VocabSizeOverflow(#[source] std::num::TryFromIntError),
164    /// The token index does not fit into a u32.
165    #[error("token_index does not fit into u32: {0}")]
166    TokenIndexOverflow(#[source] std::num::TryFromIntError),
167}
168
169/// Errors that can occur when initializing a grammar sampler
170#[derive(Debug, Eq, PartialEq, thiserror::Error)]
171pub enum GrammarError {
172    /// The grammar root was not found in the grammar string
173    #[error("Grammar root not found in grammar string")]
174    RootNotFound,
175    /// The trigger word contains null bytes
176    #[error("Trigger word contains null bytes: {0}")]
177    TriggerWordNullBytes(NulError),
178    /// The grammar string or root contains null bytes
179    #[error("Grammar string or root contains null bytes: {0}")]
180    GrammarNullBytes(NulError),
181    /// A string contains null bytes
182    #[error("String contains null bytes: {0}")]
183    NulError(#[from] NulError),
184    /// The grammar call returned null
185    #[error("Grammar initialization failed: {0}")]
186    NullGrammar(String),
187    /// An integer value exceeded the allowed range
188    #[error("Integer overflow: {0}")]
189    IntegerOverflow(String),
190    /// An error from the llguidance library
191    #[error("llguidance error: {0}")]
192    LlguidanceError(String),
193}
194
195/// Errors that can occur when creating a sampling configuration.
196#[derive(Debug, Eq, PartialEq, thiserror::Error)]
197pub enum SamplingError {
198    /// An integer value exceeded the allowed range
199    #[error("Integer overflow: {0}")]
200    IntegerOverflow(String),
201}
202
203/// Errors that can occur when sampling a token.
204#[derive(Debug, Eq, PartialEq, thiserror::Error)]
205pub enum SampleError {
206    /// A C++ exception was thrown during sampling
207    #[error("C++ exception during sampling: {0}")]
208    CppException(String),
209
210    /// An invalid argument was passed to the sampler
211    #[error("Invalid argument passed to sampler")]
212    InvalidArgument,
213}
214
215/// Decode a error from llama.cpp into a [`DecodeError`].
216impl From<NonZeroI32> for DecodeError {
217    fn from(value: NonZeroI32) -> Self {
218        match value.get() {
219            1 => Self::NoKvCacheSlot,
220            2 => Self::Aborted,
221            -1 => Self::NTokensZero,
222            error_code => Self::Unknown(error_code),
223        }
224    }
225}
226
227/// Encode a error from llama.cpp into a [`EncodeError`].
228impl From<NonZeroI32> for EncodeError {
229    fn from(value: NonZeroI32) -> Self {
230        match value.get() {
231            1 => Self::NoKvCacheSlot,
232            -1 => Self::NTokensZero,
233            error_code => Self::Unknown(error_code),
234        }
235    }
236}
237
238/// An error that can occur when loading a model.
239#[derive(Debug, Eq, PartialEq, thiserror::Error)]
240pub enum LlamaModelLoadError {
241    /// There was a null byte in a provided string and thus it could not be converted to a C string.
242    #[error("null byte in string {0}")]
243    NullError(#[from] NulError),
244    /// llama.cpp returned a nullptr - this could be many different causes.
245    #[error("null result from llama cpp")]
246    NullResult,
247    /// Failed to convert the path to a rust str. This means the path was not valid unicode
248    #[error("failed to convert path {0} to str")]
249    PathToStrError(PathBuf),
250    /// The model file does not exist at the given path.
251    #[error("model file not found: {0}")]
252    FileNotFound(PathBuf),
253}
254
255/// An error that can occur when loading a model.
256#[derive(Debug, Eq, PartialEq, thiserror::Error)]
257pub enum LlamaLoraAdapterInitError {
258    /// There was a null byte in a provided string and thus it could not be converted to a C string.
259    #[error("null byte in string {0}")]
260    NullError(#[from] NulError),
261    /// llama.cpp returned a nullptr - this could be many different causes.
262    #[error("null result from llama cpp")]
263    NullResult,
264    /// Failed to convert the path to a rust str. This means the path was not valid unicode
265    #[error("failed to convert path {0} to str")]
266    PathToStrError(PathBuf),
267    /// The adapter file does not exist at the given path.
268    #[error("adapter file not found: {0}")]
269    FileNotFound(PathBuf),
270}
271
272/// An error that can occur when loading a model.
273#[derive(Debug, Eq, PartialEq, thiserror::Error)]
274pub enum LlamaLoraAdapterSetError {
275    /// llama.cpp returned a non-zero error code.
276    #[error("error code from llama cpp")]
277    ErrorResult(i32),
278}
279
280/// An error that can occur when loading a model.
281#[derive(Debug, Eq, PartialEq, thiserror::Error)]
282pub enum LlamaLoraAdapterRemoveError {
283    /// llama.cpp returned a non-zero error code.
284    #[error("error code from llama cpp")]
285    ErrorResult(i32),
286}
287
288/// An error that can occur when converting a token to a string.
289#[derive(Debug, thiserror::Error, Clone)]
290#[non_exhaustive]
291pub enum TokenToStringError {
292    /// the token type was unknown
293    #[error("Unknown Token Type")]
294    UnknownTokenType,
295    /// There was insufficient buffer space to convert the token to a string.
296    #[error("Insufficient Buffer Space {0}")]
297    InsufficientBufferSpace(c_int),
298    /// The token was not valid utf8.
299    #[error("FromUtf8Error {0}")]
300    FromUtf8Error(#[from] FromUtf8Error),
301    /// An integer conversion failed.
302    #[error("Integer conversion error: {0}")]
303    IntConversionError(#[from] std::num::TryFromIntError),
304}
305
306/// Failed to convert a string to a token sequence.
307#[derive(Debug, thiserror::Error)]
308pub enum StringToTokenError {
309    /// the string contained a null byte and thus could not be converted to a c string.
310    #[error("{0}")]
311    NulError(#[from] NulError),
312    #[error("{0}")]
313    /// Failed to convert a provided integer to a [`c_int`].
314    CIntConversionError(#[from] std::num::TryFromIntError),
315}
316
317/// Failed to apply model chat template.
318#[derive(Debug, thiserror::Error)]
319pub enum NewLlamaChatMessageError {
320    /// the string contained a null byte and thus could not be converted to a c string.
321    #[error("{0}")]
322    NulError(#[from] NulError),
323}
324
325/// Failed to apply model chat template.
326#[derive(Debug, thiserror::Error)]
327pub enum ApplyChatTemplateError {
328    /// the string could not be converted to utf8.
329    #[error("{0}")]
330    FromUtf8Error(#[from] FromUtf8Error),
331    /// An integer conversion failed.
332    #[error("Integer conversion error: {0}")]
333    IntConversionError(#[from] std::num::TryFromIntError),
334}
335
336/// Failed to detect tool-call diagnostic markers for a model.
337#[derive(Debug, thiserror::Error)]
338pub enum MarkerDetectionError {
339    /// llama.cpp returned an error code from the marker detection FFI call.
340    #[error("ffi error {0}")]
341    FfiError(i32),
342    /// The C++ side threw an exception during template analysis.
343    #[error("c++ exception during template analysis: {0}")]
344    AnalyzeException(String),
345    /// llama.cpp returned a marker string but its bytes were not valid UTF-8.
346    #[error("ffi returned non-utf8 marker bytes: {0}")]
347    MarkerUtf8Error(#[from] FromUtf8Error),
348}
349
350/// Failed to parse a chat message via [`crate::Model::parse_chat_message`].
351#[derive(Debug, thiserror::Error)]
352pub enum ParseChatMessageError {
353    /// llama.cpp returned an error code from the parse FFI call.
354    #[error("ffi error {0}")]
355    FfiError(i32),
356    /// The C++ side threw an exception while parsing.
357    #[error("c++ exception during chat parse: {0}")]
358    ParseException(String),
359    /// An accessor returned bytes that were not valid UTF-8.
360    #[error("ffi returned non-utf8 string: {0}")]
361    StringUtf8Error(#[from] FromUtf8Error),
362    /// The caller passed a `tools_json` argument that is not valid JSON.
363    #[error("tools_json is not valid JSON: {0}")]
364    ToolsJsonInvalid(#[source] serde_json::Error),
365    /// The caller passed a `tools_json` argument that parses as JSON but is not an array.
366    #[error("tools_json must be a JSON array")]
367    ToolsJsonNotArray,
368    /// Failed to serialize the tools array for the FFI call.
369    #[error("could not serialize tools to JSON: {0}")]
370    ToolsSerialization(String),
371    /// The model has no usable chat template, so the parser cannot be built.
372    #[error("model has no chat template")]
373    NoChatTemplate,
374    /// The wrapper-side fallback parser detected a structural issue while parsing the body.
375    #[error("template-override fallback parser failed: {0}")]
376    TemplateOverrideFailed(#[from] ToolCallFormatFailure),
377}
378
379/// Top-level failure for the wrapper-side template-override parsers (one variant per supported shape).
380#[derive(Debug, thiserror::Error)]
381pub enum ToolCallFormatFailure {
382    #[error("bracketed-args fallback parser: {0}")]
383    BracketedArgs(#[from] BracketedArgsFailure),
384    #[error("json-object fallback parser: {0}")]
385    JsonObject(#[from] JsonObjectFailure),
386    #[error("key-value-xml-tags fallback parser: {0}")]
387    KeyValueXmlTags(#[from] KeyValueXmlTagsFailure),
388    #[error("paired-quote fallback parser: {0}")]
389    PairedQuote(#[from] PairedQuoteFailure),
390    #[error("xml-function-tags fallback parser: {0}")]
391    XmlFunctionTags(#[from] XmlFunctionTagsFailure),
392}
393
394/// Failures specific to the JSON-object args parser (Qwen 3 `<tool_call>{"name":..., "arguments":...}</tool_call>`).
395#[derive(Debug, thiserror::Error)]
396pub enum JsonObjectFailure {
397    #[error("tool call body has malformed JSON: {message}")]
398    InvalidJson { message: String },
399}
400
401/// Failures specific to the bracketed-JSON args parser (Mistral 3 `[TOOL_CALLS]name[ARGS]{...}`).
402#[derive(Debug, thiserror::Error)]
403pub enum BracketedArgsFailure {
404    #[error("tool call '{tool_name}' arguments are not valid JSON: {message}")]
405    InvalidJsonArguments { tool_name: String, message: String },
406    #[error("tool call '{tool_name}' arguments truncated before JSON value completed")]
407    UnterminatedArguments { tool_name: String },
408}
409
410/// Failures specific to the paired-quote args parser (Gemma 4 `<|tool_call>call:name{key:<|"|>val<|"|>}`).
411#[derive(Debug, thiserror::Error)]
412pub enum PairedQuoteFailure {
413    #[error("empty key in tool call '{tool_name}' arguments")]
414    EmptyKey { tool_name: String },
415    #[error("tool call '{tool_name}' translated arguments are not valid JSON: {message}")]
416    InvalidJsonArguments { tool_name: String, message: String },
417    #[error("tool call '{tool_name}' has unclosed quoted value for key '{key}'")]
418    UnclosedQuotedValue { tool_name: String, key: String },
419    #[error("tool call '{tool_name}' arguments ended without close marker (state: {state})")]
420    UnclosedArgumentBlock {
421        tool_name: String,
422        state: &'static str,
423    },
424    #[error(
425        "tool call '{tool_name}' has unexpected character '{character}' after value for key '{key}'"
426    )]
427    UnexpectedCharAfterValue {
428        tool_name: String,
429        key: String,
430        character: char,
431    },
432}
433
434/// Failures specific to the key-value XML-tags parser (GLM-4.7 `<tool_call>{name}<arg_key>{k}</arg_key><arg_value>{v}</arg_value>...</tool_call>`).
435#[derive(Debug, thiserror::Error)]
436pub enum KeyValueXmlTagsFailure {
437    #[error("tool call function tag has empty name")]
438    EmptyFunctionName,
439    #[error("tool call function block is missing close tag '{expected_close}'")]
440    UnclosedFunctionBlock { expected_close: String },
441    #[error("tool call function '{function_name}' has key tag with empty content")]
442    EmptyKey { function_name: String },
443    #[error("tool call function '{function_name}' is missing key close tag '{expected_close}'")]
444    UnclosedKeyTag {
445        function_name: String,
446        expected_close: String,
447    },
448    #[error(
449        "tool call function '{function_name}' key '{key}' is missing value open tag '{expected_open}'"
450    )]
451    MissingValueTag {
452        function_name: String,
453        key: String,
454        expected_open: String,
455    },
456    #[error(
457        "tool call function '{function_name}' key '{key}' is missing value close tag '{expected_close}'"
458    )]
459    UnclosedValueTag {
460        function_name: String,
461        key: String,
462        expected_close: String,
463    },
464}
465
466/// Failures specific to the XML function-tags parser (Qwen 3.5+ `<function=name><parameter=key>val</parameter></function>`).
467#[derive(Debug, thiserror::Error)]
468pub enum XmlFunctionTagsFailure {
469    #[error("tool call function tag has empty name")]
470    EmptyFunctionName,
471    #[error("tool call function '{function_name}' is missing close tag '{expected_close}'")]
472    UnclosedFunctionBlock {
473        function_name: String,
474        expected_close: String,
475    },
476    #[error("tool call function '{function_name}' has parameter with empty name")]
477    EmptyParameterName { function_name: String },
478    #[error(
479        "tool call function '{function_name}' parameter '{parameter_name}' is missing close tag '{expected_close}'"
480    )]
481    UnclosedParameterBlock {
482        function_name: String,
483        parameter_name: String,
484        expected_close: String,
485    },
486}
487
488/// Failed to evaluate multimodal chunks through the request classifier.
489#[derive(Debug, thiserror::Error)]
490pub enum EvalMultimodalChunksError {
491    /// `MtmdInputChunks::eval_chunks` returned an error.
492    #[error("{0}")]
493    EvalFailed(#[from] MtmdEvalError),
494    /// A chunk reported a type that is not known to this binding.
495    #[error("{0}")]
496    UnknownChunkType(#[from] MtmdInputChunkTypeError),
497    /// A chunk index that was within `chunks.len()` returned `None` from `chunks.get(index)`.
498    #[error("chunk index {0} out of bounds during post-eval walk")]
499    ChunkOutOfBounds(usize),
500}
501
502/// Failed to accept a token in a sampler.
503#[derive(Debug, thiserror::Error)]
504pub enum SamplerAcceptError {
505    /// A C++ exception was thrown during accept
506    #[error("C++ exception during sampler accept: {0}")]
507    CppException(String),
508
509    /// An invalid argument was passed (null sampler or null error pointer)
510    #[error("Invalid argument passed to sampler accept")]
511    InvalidArgument,
512}
513
514/// Errors that can occur when modifying model parameters.
515#[derive(Debug, Eq, PartialEq, thiserror::Error)]
516pub enum ModelParamsError {
517    /// The internal override vector has no available slot.
518    #[error("No available slot in override vector")]
519    NoAvailableSlot,
520    /// The first override slot is not empty.
521    #[error("Override slot is not empty")]
522    SlotNotEmpty,
523    /// A character in the key is not a valid C char.
524    #[error("Invalid character in key: byte {byte}, {reason}")]
525    InvalidCharacterInKey {
526        /// The byte value that failed conversion.
527        byte: u8,
528        /// The reason the conversion failed.
529        reason: String,
530    },
531}
532
533/// Failed to sample a token from the data array.
534#[derive(Debug, Eq, PartialEq, thiserror::Error)]
535pub enum TokenSamplingError {
536    /// The sampler did not select any token.
537    #[error("No token was selected by the sampler")]
538    NoTokenSelected,
539}
540
541/// Returned by [`crate::model::params::LlamaModelParams::fit_params`].
542#[derive(Debug, Clone, Copy, Eq, PartialEq, thiserror::Error)]
543pub enum FitError {
544    /// Could not find allocations that fit available memory.
545    #[error("could not find allocations that fit available memory")]
546    Failure,
547    /// A hard error occurred during fitting (e.g. model not found at the specified path,
548    /// or the C++ wrapper threw an exception).
549    #[error("hard error during parameter fitting")]
550    Error,
551}
552
553#[cfg(test)]
554mod tests {
555    use std::num::NonZeroI32;
556
557    use super::{DecodeError, EncodeError};
558
559    #[test]
560    fn decode_error_no_kv_cache_slot() {
561        let error = DecodeError::from(NonZeroI32::new(1).expect("1 is non-zero"));
562
563        assert_eq!(error, DecodeError::NoKvCacheSlot);
564        assert_eq!(error.to_string(), "Decode Error 1: NoKvCacheSlot");
565    }
566
567    #[test]
568    fn decode_error_n_tokens_zero() {
569        let error = DecodeError::from(NonZeroI32::new(-1).expect("-1 is non-zero"));
570
571        assert_eq!(error, DecodeError::NTokensZero);
572        assert_eq!(error.to_string(), "Decode Error -1: n_tokens == 0");
573    }
574
575    #[test]
576    fn decode_error_aborted() {
577        let error = DecodeError::from(NonZeroI32::new(2).expect("2 is non-zero"));
578
579        assert_eq!(error, DecodeError::Aborted);
580        assert_eq!(error.to_string(), "Decode Error 2: Aborted");
581    }
582
583    #[test]
584    fn decode_error_unknown() {
585        let error = DecodeError::from(NonZeroI32::new(42).expect("42 is non-zero"));
586
587        assert_eq!(error, DecodeError::Unknown(42));
588        assert_eq!(error.to_string(), "Decode Error 42: unknown");
589    }
590
591    #[test]
592    fn encode_error_no_kv_cache_slot() {
593        let error = EncodeError::from(NonZeroI32::new(1).expect("1 is non-zero"));
594
595        assert_eq!(error, EncodeError::NoKvCacheSlot);
596        assert_eq!(error.to_string(), "Encode Error 1: NoKvCacheSlot");
597    }
598
599    #[test]
600    fn encode_error_n_tokens_zero() {
601        let error = EncodeError::from(NonZeroI32::new(-1).expect("-1 is non-zero"));
602
603        assert_eq!(error, EncodeError::NTokensZero);
604        assert_eq!(error.to_string(), "Encode Error -1: n_tokens == 0");
605    }
606
607    #[test]
608    fn encode_error_unknown() {
609        let error = EncodeError::from(NonZeroI32::new(99).expect("99 is non-zero"));
610
611        assert_eq!(error, EncodeError::Unknown(99));
612        assert_eq!(error.to_string(), "Encode Error 99: unknown");
613    }
614}