Skip to content

Troubleshooting & FAQ

Common failures and questions, each as symptom, cause and fix. It is for anyone whose serialkompat run failed, was skipped, or printed something unexpected. For what a finding means, see Got a BREAK?; for exit codes and flags, see Tasks & CLI.

Quoted messages are copied from the source. Where an entry has no message of its own (the failure is Gradle's, git's or GitHub's), it says so and describes what you'll see.

The baseline extraction fails on the first PR

Symptom: The check fails on the PR that adds the plugin, with one of:

serialkompat: baseline extraction failed in worktree:
<output of the nested Gradle build>
serialkompat: the baseline ref has no serialkompat configuration to extract — 'serialkompatExtract' produced no snapshot there (expected <path> or <path>). Check that the baseline ref's committed discovery/types configuration declares at least one type to check.

Cause: The baseline is extracted by running your build at baselineRef, and that ref doesn't have the plugin, or has it without any types or discovery configured. The nested build either fails (the first message, and the log below it shows why) or skips extraction and writes no snapshot (the second).

Fix: Skip the check for the one change that adds serialkompat and merge it. See Quick start → Your first PR, which also covers the variants of this failure.

Shallow clone, or the baseline ref can't be found

Symptom: The check fails before extracting anything, with either of:

serialkompat: could not auto-detect a default baseline branch (looked for origin/HEAD, origin/main, origin/master, main, master). Set serialkompat { baselineRef.set("<ref>") } or pass -Pserialkompat.ref=<ref>.
git rev-parse --verify <ref>^{commit} failed (exit 128): <git's own output>

The first is serialkompat's. The second is the prefix serialkompat puts on any failed git command; the text after the colon is git's.

Cause: The baseline is a git ref, checked out in a temporary worktree. A shallow checkout, such as the default actions/checkout with fetch-depth: 1, doesn't fetch the ref (typically origin/main) at all, so there is nothing to resolve.

Fix: Fetch full history: fetch-depth: 0 on actions/checkout in GitHub Actions, GIT_DEPTH: 0 or git fetch --unshallow elsewhere. Details are in CI setup. If the ref has a different name, set baselineRef or pass -Pserialkompat.ref=<ref>.

jsonInstance / cborInstance fails to load

Symptom: serialkompatExtract fails with:

serialkompat: could not load the configured jsonInstance '<fqn>'. Expected `owner.member` naming a kotlinx.serialization.json.Json reachable on the runtime classpath (an object property or a top-level val). Fix or unset serialkompat.jsonInstance.

For CBOR the message names cborInstance and kotlinx.serialization.cbor.Cbor. Two related errors:

serialkompat: cborInstance is set but format is JSON. Set format = CBOR, or unset cborInstance.
serialkompat: jsonInstance is set but format is CBOR. Use cborInstance for a CBOR module.

Cause: The name doesn't resolve to a Json (or Cbor) value on the module's runtime classpath. serialkompat fails rather than falling back to the default Json, because the instance's settings decide the verdicts, and checking against the wrong wire contract would be worse than failing. Usual reasons: a typo, the object or file living in a different module, or the member not being a Json.

Fix: Point jsonInstance at package.Owner.member, where the owner is an object (or a file's generated class) and the member is a property holding the instance. Keep the name in step if you move it. The option is described in Configuration.

Every task was skipped

Symptom: serialkompatCheck and the other tasks show as SKIPPED and nothing is checked. There is no serialkompat message; Gradle prints the skip.

Cause: The tasks only run when types is non-empty or discovery is not EXPLICIT (the onlyIf in SerialkompatPlugin.kt). Applying the plugin with neither set is a deliberate no-op.

Fix: List the root types in types, or turn on a discovery mode. See Adopting gradually. A module with neither will stay skipped, which is correct for modules that hold no wire models.

The baseline is empty

Symptom: The check fails and the console starts with:

serialkompat: the baseline produced 0 contracts but the current schema has N — refusing to pass (check baselineRef/types, or set failOnEmptyBaseline=false if this is first-time adoption).

Cause: The baseline ref has no analysable types while the current code does. That would otherwise diff as "everything added, so safe" and hide any real removal. This is the failOnEmptyBaseline guard, on by default. The common cases are a baselineRef that predates your @Serializable types, and a types list or discovery config that is wrong at that ref.

Fix: If it's a mistake, fix baselineRef or types. If you're adopting serialkompat for types that don't exist at the baseline yet, set failOnEmptyBaseline.set(false) for that one change and remove it afterwards. See Quick start. The CLI equivalent is --allow-empty-baseline.

A COVERAGE_GAP warning after upgrading to 0.3.1

Symptom: After upgrading to 0.3.1, a check against a baseline recorded by 0.3.0 or earlier reports a COVERAGE_GAP WARN for fields marked @Contextual. There is no break and the build passes.

Cause: 0.3.1 started analysing @Contextual fields that earlier versions recorded as unanalysable. The old baseline has no shape to compare with, so each newly resolved field gets one WARN instead of a false type change or CONTRACT_REMOVED (see the changelog).

Fix: None is needed. This is expected once per previously-unresolved field. The next baseline is recorded by the new version and compares fully. If a @Contextual field is still reported after that, it has no module entry and no fallback serializer; see @Contextual resolution.

The first run is slow

Symptom: The first check on a commit takes much longer than later ones. No message.

Cause: The baseline is extracted by running a nested Gradle build in a temporary git worktree at baselineRef, which compiles your models at that commit. The result is cached per module, keyed by commit SHA and serialkompat version.

Fix: Nothing to fix; later runs against the same commit and version reuse the cache. In CI, keep Gradle's own caches (and the build cache, since serialkompatExtract is cacheable) between runs. After a serialkompat upgrade the baseline is re-extracted once. See Baseline & caching.

The sticky comment fails with a 403

Symptom: The Action step that posts the pull-request comment fails with an HTTP 403 from GitHub. The message is GitHub's, not serialkompat's, and the compatibility check itself isn't the cause.

Cause: The calling workflow's token can't write pull-request comments, because the job lacks the permission.

Fix: Add permissions: pull-requests: write to the job. See CI setup for the Action's requirements and the sticky comment.

In a multi-module build, one failing module stops the others

Symptom: With several modules using serialkompat, the build stops at the first module whose check fails, and the other modules' checks never run, so you see only one module's findings.

Cause: This is Gradle's default, not serialkompat's: when a task fails, Gradle stops scheduling further tasks unless told otherwise.

Fix: Run with --continue (for example ./gradlew serialkompatCheck --continue) to run every module's check and report all failures at the end. The sticky comment still shows one module's report; see the Action in a multi-module build.