{"record":{"id":"05c4a71968190518","repo":"denoland/deno","slug":"cannot-use-non-object-value-with-an-internally-tag","errorCode":null,"errorMessage":"cannot use non-object value with an internally tag enum","messagePattern":"cannot use non-object value with an internally tag enum","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/ops/conversion/to_v8/enum.rs","lineNumber":120,"sourceCode":"              Ok(::deno_core::v8::Object::with_prototype_and_properties(\n                __scope,\n                __null,\n                __keys,\n                __converters,\n              ).into())\n            }\n          }\n          EnumMode::InternallyTagged { tag } => {\n            quote! {\n              if Ok(__obj_body) = __body.try_cast::<::deno_core::v8::Object>() {\n                let __tag_key = #tag;\n                let __tag_value = #tag_value;\n\n                 __obj_body.set(__scope, __tag_key, __tag_value);\n\n                Ok(__obj_body.into())\n              } else {\n                panic!(\"cannot use non-object value with an internally tag enum\");\n              }\n            }\n          }\n          EnumMode::AdjacentlyTagged { tag, content } => {\n            quote! {\n              let __null = ::deno_core::v8::null(__scope).into();\n              let __keys = &[#tag, #content];\n              let __converters = &[#tag_value, __body];\n\n              Ok(::deno_core::v8::Object::with_prototype_and_properties(\n                __scope,\n                __null,\n                __keys,\n                __converters,\n              ).into())\n            }\n          }\n          EnumMode::Untagged => {","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/libs/ops/conversion/to_v8/enum.rs#L102-L138","documentation":"deno_core's #[op2] codegen converts Rust enums marked #[serde(tag = \"...\")] (internally tagged) into v8 objects by inserting the tag key into the serialized variant body. The generated code requires that body to convert to a v8::Object; when a variant serializes to a non-object value (string, number, array), it panics with this message.","triggerScenarios":"Defining an op whose parameter or return type is an internally tagged enum where a variant's serialized payload is not object-shaped - e.g. a newtype variant wrapping a primitive, or a variant whose serde serialization yields a scalar instead of a map.","commonSituations":"Sharing serde enum types between HTTP handlers and deno ops; adding #[serde(tag)] to an existing enum already crossing an op boundary; refactoring struct variants into tuple variants.","solutions":["Switch to adjacently tagged encoding: #[serde(tag = \"...\", content = \"...\")], which the codegen handles by wrapping the body in an object","Restructure the enum so every data-carrying variant is a struct variant (serializes as a map)","Pass the value as a plain struct or serde_json::Value instead of a tagged enum across the op boundary","If the enum is unit-only, verify every variant path produces the tag object before calling the op"],"exampleFix":"// before\n#[derive(Serialize)]\n#[serde(tag = \"kind\")]\nenum Event {\n  Ping(u64), // body is a number -> panic in generated code\n}\n\n// after\n#[derive(Serialize)]\n#[serde(tag = \"kind\", content = \"data\")]\nenum Event {\n  Ping(u64), // { \"kind\": \"Ping\", \"data\": 1 }\n}","handlingStrategy":"validation","validationCode":"// Before returning a tagged enum from an op, confirm it serializes object-shaped\nlet v = serde_json::to_value(&event).map_err(|e| e.to_string())?;\nif !v.is_object() {\n  // restructure the enum (adjacently tag it) before crossing the op boundary\n  return Err(\"internally tagged enum variant is not object-shaped\");\n}","typeGuard":"fn serializes_as_object<T: serde::Serialize>(v: &T) -> bool {\n  serde_json::to_value(v).map(|j| j.is_object()).unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Prefer adjacently tagged enums ([serde(tag = ..., content = ...)]) in op signatures","Add a unit test per op signature that round-trips every enum variant through serialization","Keep enums crossing op boundaries limited to struct variants and unit variants"],"tags":["deno-core","op2","serde","enum","codegen","v8"],"backgroundTag":"enum-serialization-mismatch","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}