Skip to content

When an exercise does not work

Every course on this site ends its chapters with something to run. They all assume the same stack in the same state, so when one fails it is almost always the state and not the exercise. This page is the short list, in the order the failures actually happen.

The exercise returns no rows

Usually not a bug in the query.

make setup is not enough for these courses. It seeds world reference data and four demo accounts, which leaves item, recommended_route and heat_point empty, and leaves coverage_poi not existing at all, because the coverage batch creates that table rather than a migration. One more target fills them from data already committed to the repository, with no network:

make course-data

Course 1's own Try it box is the check: 736 items and 10 coverage rows on a fresh clone. If you get those two numbers, the seed is fine and the empty result is telling you something real about the query.

A second, quieter cause: several exercises select by a seeded name rather than an id, because ids differ on every install. If you renamed or re-seeded a pin, the WHERE clause matches nothing.

relation "coverage_poi" does not exist

The coverage batch has never run on this stack. make course-data runs it against a committed fixture, which creates the table and loads ten rows. Nothing else creates it.

make course-data fails partway

The import is one transaction, so a failure anywhere rolls the whole thing back and the later steps never run. Read the first error rather than the last line.

No geometry kind for letter X means the Makefile's export and the importer disagree about a catalogue letter. That exact failure shipped once and went unnoticed for two weeks, which is why tools/check-course-data.py now runs in CI and in make wiki-check. Run it directly for a precise answer:

python3 tools/check-course-data.py

The stack answers on the wrong port, or not at all

The ports the exercises use, as the compose file maps them:

what port
the app, and the public API 8001
the wiki you are reading 8013
Postgres, from the host 5433
MinIO, where tiles are published 9100
the pipeline container's own endpoint 8012

Every database exercise goes through the container rather than the host port, which is why they all start docker compose -f developers/docker/compose.yaml exec db psql -U cc -d cyclingcommons. If that command fails, nothing further on the page will work.

The map draws nothing, or draws almost nothing

Read the published manifest before anything else:

curl -s http://localhost:9100/cc-maps/coverage/manifest.json | jq '{url, regions: (.regions|length), counts}'

A manifest naming one region with a handful of counts means something republished over your real index. The usual cause is a coverage run against a fixture, including the one inside make course-data, because the point arm publishes even when --no-publish is passed. Put it back with make coverage-tiles, which rebuilds and publishes from the rows already in PostGIS in about five minutes, and not with make coverage-refresh, which re-harvests every region over the network.

A 404 on that URL means no coverage tiles have ever been published on this stack.

A harvest exits non-zero

exit what it means what to do
2 another coverage run holds the advisory lock wait for it, or find the run that never finished
1, with a drift message the new extract is more than 40% smaller than the last good one investigate the download; do not lower the threshold
1, unseeded country the country's regions are not seeded, so the ownership filter would drop every row onboard the country first
1, md5 mismatch Geofabrik's mirror is mid-update and its checksum does not match its file wait and re-run

After any harvest, one number is a pass or fail rather than a judgement: SELECT count(*) FROM coverage_poi WHERE region_id IS NULL must be zero. Anything else means rows landed outside every region polygon, and those rows are invisible to every scoped query on the site.

The elevation exercises answer zero

A DEM that has no tile for a place does not raise an error. It answers 0, and zero is a number, so it flows all the way through to a published climb reading flat. There are three ways to be absent and all of them are silent: no tiles installed, the instance not restarted after installing them, or the instance not named in ELEVATION_URLS.

Ask for a summit whose height you know rather than a random point, and treat a zero as a failure rather than an answer. Building elevation tiles has the check and the fix.

The API exercises fail from a browser but work in curl

Two headers, and both are on the tile host rather than the API. A browser reading byte ranges out of a PMTiles archive needs Access-Control-Allow-Origin and Content-Range exposed. A host that allows the origin but hides the range header serves a map that silently draws nothing.

If the request is to /v1/ and comes back 429, read Retry-After and wait. The limit is per client address, and the exercises are well under it unless something is looping.

Nothing here matches

Two commands answer most of what is left:

make wiki-check    # the wiki builds, its code quotes match, the numbers page is current
make check-env     # warns when the running app image no longer matches the repo's Dockerfile

The second one catches a class of failure that reads like a product bug: an image built before a Dockerfile change, still running, with settings the repository no longer declares.