.agents/skills/update-skia/references/known-gotchas.md
Hard-won findings from past Skia milestone updates. Check these proactively — they will save hours of debugging.
DEF_STRUCT_MAP vs Type AliasesWhen upstream changes a C++ type from a struct to a using alias (e.g., GrVkYcbcrConversionInfo → using VulkanYcbcrConversionInfo), the DEF_STRUCT_MAP macro in sk_types_priv.h forward-declares struct X, which conflicts with the alias. Fix by switching to DEF_MAP_WITH_NS(namespace, ActualType, CType) and wrapping in the appropriate platform guard (e.g., #if SK_VULKAN).
static_assert Failuressk_structs.cpp asserts sizeof(our_c_type) == sizeof(CppType) for every struct mapped via reinterpret_cast. When upstream adds fields (e.g., SkPngEncoder::Options gaining gainmap pointers in m133), the assert fires. During analysis, proactively check every asserted struct against the target milestone — don't wait for the build to catch it.
SkiaSharp adds custom methods to upstream headers (e.g., SkTypeface::RefDefault()). After merge, implementations in .cpp files may survive but header declarations can be silently removed by upstream changes.
When header declarations are lost:
src/c/) to avoid this recurring conflict#ifdef GuardsPlatform font manager calls (e.g., SkFontMgr_New_FontConfig) must be guarded by feature-availability macros (SK_FONTMGR_FONTCONFIG_AVAILABLE), not platform macros (SK_BUILD_FOR_UNIX). When skia_use_fontconfig=false, platform macros leave the symbol unresolved. The -Wl,--no-undefined linker flag catches this at link time.
git-sync-deps emsdk FailureUpstream m121+ added an activate-emsdk call in tools/git-sync-deps. Since SkiaSharp comments out emsdk in DEPS (the fork builds WASM against an external, pre-activated emsdk via skia_emsdk_dir), an unguarded activate-emsdk would try to install/activate an in-tree emsdk that does not exist. The fix is in the submodule: bin/activate-emsdk returns early when third_party/externals/emsdk is absent, making the call a no-op for every caller (cake builds, the review skill, direct tools/git-sync-deps runs). No GIT_SYNC_DEPS_SKIP_EMSDK env var is needed.
BUILD.gn Legacy FlagsUpstream progressively deprecates legacy APIs behind flags (e.g., SK_DEFAULT_TYPEFACE_IS_EMPTY, SK_DISABLE_LEGACY_DEFAULT_TYPEFACE). When these flags break SkiaSharp's C API:
Also watch for renamed/removed GN flags between milestones — obsolete flags cause Unknown GN flag errors. Always diff the target BUILD.gn against the current one.
.gitmodules Branch NameWhen the mono/skia target branch name changes, .gitmodules must be updated to track the new branch. Easy to forget; causes silent submodule tracking failures.
declare_args CleanupWhen removing fork-only GN args, check ALL native build scripts (native/*/build.cake) for references. Platform scripts may pass these args to GN, causing Unknown GN flag warnings.
Upstream progressively moves defines from GN to Bazel-only. Check that all #if defined(X) guards in Skia source that SkiaSharp depends on still have their defines set in the GN build. Symptoms: feature code silently compiled out, returning nullptr/fallback.
:coreThe C API shims (src/c/gr_context.cpp etc.) compile as part of :core, but backend defines (SK_VULKAN, SK_DIRECT3D, SK_METAL) may only live on :gpu's all_dependent_configs. Verify these reach :core's own compile — if not, macros like SK_ONLY_VULKAN(expr, fallback) expand to the fallback. Add the backend defines directly to :core in BUILD.gn.
Upstream may move previously-core modules into separate optional targets. If the C API exposes functions from that module, add it as an explicit dependency of the SkiaSharp target in BUILD.gn rather than merging sources into core.
SkiaSharp's fork often has newer dependency versions than upstream. When resolving DEPS conflicts, do NOT blindly take upstream's hashes — you may downgrade and break the build.
git log --oneline skiasharp | grep -i "update\|bump\|libpng\|zlib\|expat\|brotli\|webp\|harfbuzz\|vulkan"
Keep fork's hash for customized deps. Common ones: libwebp, brotli, expat, libpng, zlib, vulkanmemoryallocator, harfbuzz.
HarfBuzz updates require hand-written C# delegate proxies and must be done via the native-dependency-update skill. During a milestone update, ALWAYS:
git checkout HEAD -- binding/HarfBuzzSharp/HarfBuzzApi.generated.csWhen upstream inserts new enum values mid-sequence, ALL subsequent values shift. This affects sk_enums.cpp, Definitions.cs, EnumMappings.cs, and any test hardcoding enum integers. Always regenerate bindings — never hand-edit enum values.
Skia relocates files, it rarely removes them. Example: src/utils/SkJSON.h → modules/jsonreader/SkJSONReader.h in m133. Always search the target branch for where content moved before removing references:
git ls-tree -r upstream/chrome/m{TARGET} --name-only | grep -i "FILENAME_STEM"
A symbol on a diff - line may have been moved within the same file, not removed. Always confirm on the target branch:
git show upstream/chrome/m{TARGET}:FILEPATH | grep "SYMBOL"
Never use a tree-override merge (git merge -s ours, git read-tree --reset). This destroys git blame attribution for all C API files. Always resolve each conflict individually. Use git merge --no-commit for manual control.
| File Category | Strategy |
|---|---|
BUILD.gn | Combine both — most complex; keep upstream structure AND SkiaSharp's platform flags/targets |
DEPS | Combine — keep our dependency pins, accept upstream structure |
RELEASE_NOTES.md, infra/ | Take upstream |
C API headers (include/c/) | Keep SkiaSharp — these don't exist upstream |
C API source (src/c/) | Keep SkiaSharp + adapt — fix includes and API calls in post-merge commits |
Other upstream source (src/, include/) | Check history first — see gotcha #15 |
--theirs Without Checking File HistoryFailure mode: A merge conflict in an upstream file (outside src/c/ / include/c/) is resolved
with git checkout --theirs, silently overwriting an intentional SkiaSharp fork patch.
Mandatory process for EVERY conflicted file:
# BEFORE resolving, check if the fork has intentional patches
git log --oneline skiasharp -- <conflicted-file>
--theirs is likely safe.git checkout --theirs as a shortcut for files you haven't investigated.Key signal words in commit messages that indicate intentional fork patches:
Restore, patch, fix for, platform, workaround, SkiaSharp, iOS, Tizen
InvalidOperationException: The version of the native libSkiaSharp library (X) is incompatible means VERSIONS.txt wasn't fully updated. Fix the root cause — do NOT work around it.
Upstream periodically improves color conversion precision, shifting expected pixel values by ±1. When pixel-exact test assertions break, check if upstream changed the conversion and update expected values.
Tests use Skip.If() for unsupported platforms. Run dotnet test tests/SkiaSharp.Tests.Console.sln for the full suite. Backend-specific tests self-skip when hardware isn't available.
| Error | Cause | Fix |
|---|---|---|
EntryPointNotFoundException | Native lib not rebuilt after C API change | dotnet cake --target=externals-{platform} |
error CS0246 missing type | Binding not regenerated | pwsh ./utils/generate.ps1 |
static_assert sizeof failure | Upstream struct gained/lost fields | Update C API struct in sk_types.h |
#include file not found | Upstream moved file to new path | Search target branch, update path |
LNK2001 unresolved external | C function name mismatch or missing lib | Verify names; check system library linkage |
Unknown GN flag error | Obsolete build flag | Remove flag; diff target BUILD.gn; check native/*/build.cake |
git blame all from merge commit | Tree-override merge was used | Redo as genuine conflict-resolved merge |
| Merge conflict in DEPS | Both forks updated deps | Keep our pins, accept upstream structure |
| Enum values don't match | Mid-sequence insertion | Regenerate bindings — never hand-edit |
| Pixel mismatch by ±1 | Upstream precision change | Update expected test values |
| GPU context C API returns nullptr | Backend defines not reaching :core | Add defines to :core in BUILD.gn |
| Vulkan GRContext returns null | VMA fallback compiled out | Check SK_USE_VMA is defined in GN build |