Yes, serde_yaml is deprecated: its last release is versioned 0.9.34+deprecated, its GitHub repository was archived on 25 March 2024, and its README says “This project is no longer maintained.” Existing code keeps compiling, but no fixes will come. To replace it, either switch to a maintained fork with the same API, such as yaml_serde from the YAML organization, which takes one line in Cargo.toml, or move to a crate like oneyaml, which keeps serde_yaml’s function names and adds YAML 1.2 reading and comment-preserving edits.
The status of serde_yaml and its successors
| Crate | Latest on crates.io (26 Oct 2026) | Status, per its own page |
|---|---|---|
| serde_yaml | 0.9.34+deprecated, 25 Mar 2024 | Repository archived; README: “(This project is no longer maintained.)” (GitHub) |
| yaml_serde | 0.10.7, 18 Aug 2026 | “the actively maintained fork of serde-yaml … by the official YAML organization” (README) |
| serde_yaml_ng | 0.10.0, 26 May 2024 | A fork of serde_yaml’s last commit that aims to be “compatible as much as possible” and “only follows the YAML specification 1.1” (README) |
| serde_norway | 0.9.42, 21 Dec 2024 | “A hard-fork of Serde YAML” (README) |
| serde_yml | 0.0.13, 27 May 2026 | Its crates.io description: “DEPRECATED — serde_yml is unmaintained” |
| oneyaml | 1.2.0, 21 Oct 2026 | A separate implementation: serde plus a lossless Document, YAML 1.2 |
If a dependency warning sent you here, note that RustSec’s unmaintained advisory RUSTSEC-2024-0320 is for yaml-rust, a different crate, not for serde_yaml. RustSec’s serde_yaml page lists only a 2018 vulnerability.
Keep the API: rename to a fork
yaml_serde’s README shows how to swap it in with Cargo’s package renaming, so every use serde_yaml:: line stays as it is:
[dependencies] serde_yaml = { package = "yaml_serde", version = "0.10" }
We built a program that calls serde_yaml::from_str against this line, and Cargo resolved it to yaml_serde 0.10.7. This keeps your code and the libyaml-based behavior it was written against: in cargo tree, yaml_serde depends on libyaml-rs and serde_yaml_ng on unsafe-libyaml, both described on crates.io as “libyaml transpiled to rust by c2rust”.
Move to oneyaml: the use swap
oneyaml keeps serde_yaml’s entry-point names (from_str, from_slice, from_reader, to_string, to_writer, from_value, to_value, Value, Mapping, Number, Sequence, Error), so most code moves by changing the crate name:
cargo remove serde_yaml cargo add [email protected]
Here the same struct is read by serde_yaml_ng, standing in for serde_yaml with the same 0.9 API, and by oneyaml:
use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Config { name: String, port: u16, tags: Vec<String>, } fn main() -> Result<(), Box<dyn std::error::Error>> { let src = "name: web\nport: 8080\ntags:\n- api\n- internal\n"; // Before: serde_yaml (here its fork serde_yaml_ng, the same 0.9 API). let before: Config = serde_yaml_ng::from_str(src)?; // After: the same calls with the crate name swapped. let after: Config = oneyaml::from_str(src)?; assert_eq!(before, after); // oneyaml's own layout indents a sequence under its key ... print!("{}", oneyaml::to_string(&after)?); println!("---"); // ... and Compat::serde_yaml() writes what serde_yaml wrote. let compat = oneyaml::to_string_with(&after, &oneyaml::Compat::serde_yaml())?; assert_eq!(compat, serde_yaml_ng::to_string(&before)?); print!("{compat}"); Ok(()) }
name: web port: 8080 tags: - api - internal --- name: web port: 8080 tags: - api - internal
Both reads give the same value. The output differs in one way by default: oneyaml indents a sequence under its key. Compat::serde_yaml() writes serde_yaml’s layout, and the assertion in the program checks it byte for byte against serde_yaml_ng’s output. Use it while snapshot tests or diffs depend on the old layout.
What changes when you move to oneyaml
The migration guide lists every difference with its reason. The ones most code meets:
- YAML 1.2 by default. oneyaml reads the YAML 1.2 Core schema. For example, serde_yaml_ng 0.10.0 reads
mask: 0b1010as the number 10, and oneyaml reads it as a string, as the Core schema does.Compat::serde_yaml()brings back serde_yaml’s reading (10 here), and every YAML 1.1 behavior is a named setting. - Merge keys (
<<) are an opt-in setting,merge_keys, rather than a separateValue::apply_mergestep (anchors, aliases and merge keys). Valuekeeps more. It keeps every tag the schema doesn’t resolve, including!!binaryand!!set; holds integers of any size; and indexing never panics.from_slicedecodes UTF-8, UTF-16 and UTF-32, so it can’t lend&strfields out of the input: its target isDeserializeOwned. To borrow, usefrom_str.- Limits. Every read has resource limits; use
Limits::untrusted()for network input (untrusted input).
What you get beyond a fork
- Edits that keep the file. A
Documentchanges only what you touch, and every comment stays with its entry, which serde_yaml’sValuecan’t do (edit YAML without losing comments). - Errors with a fix. Each error has a
Y####code, a source snippet and a suggested fix (errors with line and column). - JSON Patch and merge patches on the YAML text, all or nothing (JSON Patch for YAML).
- No
unsafeand no panics on any input.
A fork is the better choice when you need serde_yaml’s exact behavior without reviewing the differences, or wall-clock read speed on files where serde_yaml_ng is faster (oneyaml’s benchmark tables name them). To weigh all the crates, see choosing a Rust YAML crate, and to start, the getting started guide.