Steam achievement sets: batched unlocks fail on unowned DLC
Valve's Achievement Sets beta lets Steam games group achievements by base game, DLC and other sets. Steam refuses unlocks for DLC the account doesn't own or for archived sets, and one rejected item can fail an entire batch write. A C# developer maintaining an achievement manager explains the schema changes, the parsing code and the ownership-check fix.
Key points
- Parse the groups section; achievements with no groupid, or an unlisted one, belong to the base game set.
- A group with a dlcappid is a DLC set; per-achievement archived is an int 1 on the bit.
- Steam rejects the whole batch if one achievement sits in an unowned DLC set or is archived.
- With Steamworks.NET, use SteamApps.BIsSubscribedApp for ownership, not BIsDlcInstalled.
- Ownership checks apply only to unlocks; locking an already earned achievement is still allowed.
- The group-level archived key name is unconfirmed and may change while the beta runs.
Valve's Achievement Sets beta lets developers split a game's achievements into groups such as the base game and each DLC, with separate progress and completion. Two changes matter for tools that read or write achievements: a new groups section in the stats schema, and a server-side rule that rejects unlocks for DLC the account does not own and for archived sets. The author's achievement manager found that one rejected item made a batched write fail entirely.
The fix is to check DLC ownership before writing. The author uses BIsSubscribedApp rather than BIsDlcInstalled, which reports owned but uninstalled DLC as unowned, and treats failed lookups as owned so a network error does not silently block unlocks.
Real-world use
- A C# tool that lists or bulk-unlocks Steam achievements across a user's library.
- A companion app that shows per-DLC completion progress for a game with expansions.
- A background sync job that reads the cached stats schema and stores achievement groups.
Why it matters
If your code reads or writes Steam achievements, a single rejected item can fail a whole batch, so ownership and archive checks belong in your code before anything is sent.
Related stories
- C# 14 extension members: the shadowing bug the compiler won't warn you aboutDEV Community: #csharp1 d ago
- LLM tokens measured in C#: why AI can't count the R's in strawberryDEV Community: #csharp2 d ago
- Collection Return Types in C#: Interfaces, Views, and SnapshotsNick Cosentino (Dev Leader)2 d ago