Skip to contents

This catalog lists common errors with likely causes and direct fixes. Errors include actionable identifiers (stage IDs, match IDs, participant counts) wherever possible.


1) Infeasible transition count

Symptom: validate() or build() fails because the selector would pick more participants than are available from the source stage.

my_spec <- spec() |>
  round_robin("groups") |>
  single_elim("finals", take = top_n(8))

validate(my_spec, n = 6)
# Error: stage 'finals' requires 8 participants via `top_n(8)` but source
# stage 'groups' can produce at most 6. Adjust the selector or increase
# participant count (currently n = 6).

Fix:

  • Reduce n in top_n(n) to fit the available pool, or
  • increase the participant count passed to validate() / build().

2) top_per_group() on a non-grouped stage

Symptom: Error when a per-group selector is used on a stage without group structure.

trn <- tournament(paste("Team", 1:8)) |>
  round_robin("league") |>
  single_elim("playoffs", take = top_per_group(2))
# Error: `top_per_group()` requires the source stage 'league' to have
# groups, but it was defined without `groups =`. Use `top_n()` for flat
# standings, or add `groups =` to the source stage.

Fix:

  • Use top_n(n) if the source stage is a flat round-robin, or
  • add groups = k to the source stage verb if group play is intended.

3) previous_stage() on the first stage

Symptom: Error when the first stage verb implicitly or explicitly uses previous_stage() but there is no preceding stage.

spec() |>
  single_elim("finals", from = previous_stage())
# Error: `previous_stage()` in stage 'finals' resolved to no prior stage.
# 'finals' is the first stage in this spec. Provide an explicit `from =`
# or add a source stage before it.

Fix: This only happens if you set from = previous_stage() explicitly on the first stage, or if you call a stage verb on an empty spec without a source. Add the source stage first:

spec() |>
  round_robin("groups") |>
  single_elim("finals", take = top_n(8))  # from = previous_stage() is implicit, resolves to "groups"

4) Explicit from = stage ID not found

Symptom: Error when a from = argument references a stage that does not exist.

tournament(paste("Team", 1:16)) |>
  round_robin("groups") |>
  single_elim("finals", from = "typo_stage", take = top_n(8))
# Error: `from = "typo_stage"` in stage 'finals' references a stage that
# does not exist. Known stages: groups. Check the stage ID spelling.

Fix: Check the stage ID spelling. Use the exact string passed to the source stage verb.


5) Overlapping selectors in a branch

Symptom: Two stages branching from the same source both select the same participants, causing a conflict.

tournament(paste("Team", 1:16)) |>
  round_robin("groups") |>
  single_elim("championship", from = "groups", take = top_n(8)) |>
  single_elim("consolation",  from = "groups", take = top_n(8))  # same 8!
# Error: stages 'championship' and 'consolation' both consume overlapping
# participants from source 'groups'. Use `remaining()` for the second
# branch to select the leftover pool, or adjust selectors to be disjoint.

Fix: Use remaining() for the second branch:

tournament(paste("Team", 1:16)) |>
  round_robin("groups") |>
  single_elim("championship", from = "groups", take = top_n(8)) |>
  single_elim("consolation",  from = "groups", take = remaining())

6) Overwrite blocked by downstream materialization

Symptom: Attempting to change a result after a downstream stage has already been materialized from it.

# After all group results have been entered and "knockout" has materialized:
trn <- trn |> result("groups", match = 1, score = c(0, 2))
# Error: result for match 1 in stage 'groups' cannot be overwritten because
# downstream stage 'knockout' has already been materialized from 'groups'.
# Use teardown(trn, "knockout") first to unlock result editing.

Fix: Use teardown() to un-materialize the blocking downstream stage, then re-enter the corrected result:

trn <- teardown(trn, "knockout")
trn <- trn |> result("groups", match = 1, score = c(0, 2))
# "knockout" will re-materialize automatically when groups is complete again.

7) validate() fails for duplicate stage IDs

Symptom: Two stage verbs use the same stage ID.

spec() |>
  round_robin("stage") |>
  single_elim("stage")  # duplicate!
# Error: stage ID 'stage' is already registered. Stage IDs must be unique.
# Provide a distinct ID for each stage.

Fix: Use unique IDs for every stage.


8) Invalid score argument

Symptom: result() receives a score that is not a numeric vector of at least length 2.

trn <- trn |> result("groups", match = 1, score = 2)
# Error: `score` must be a numeric vector of length >= 2 (e.g., c(2, 1)).
# Received: a single value (2). Did you mean score = c(2, 1)?

Fix: Always pass score as a vector: score = c(home_score, away_score).


Operational checklist

  1. Define: tournament(teams) |> <stage_verbs>.
  2. (Optional) Validate: validate(spec, n) before building.
  3. Inspect schedule: matches(trn, stage), stage_status(trn).
  4. Enter results: result(trn, stage, match, score = c(x, y)).
  5. Auto-advance is the default — check stage_status(trn) to confirm.
  6. Use teardown(trn, stage) if you need to correct results after advancement.
  7. Finish: winner(trn), rankings(trn), routing_log(trn).