The short answer: Spec Kit is GitHub's open source toolkit for spec driven
development. You install a command line tool called specify, it writes a scaffold of markdown
files into your repository, and your coding agent then fills those files in one at a time, in a fixed
order, starting with the project principles and ending with the code.
The honest answer: almost nobody loses time on the method. They lose it on step one.
On releases before v1.0, specify init called the GitHub releases API to find the current
template bundle, which made the very first command the only one that needed the network. On a rate limited
or blocked connection it died with a 403 or a 401, and you got no files at all.
That is why nearly every troubleshooting thread you will find is about init rather than about specs. Read the failures below as version scoped, because they are.
Two directories, and knowing which is which saves you a lot of confusion later.
.specify/ project level, written once
├── memory/
│ └── constitution.md the rules every later step must obey
├── templates/ the blanks the commands fill in
├── scripts/
│ ├── bash/ create-new-feature.sh, setup-plan.sh,
│ │ check-prerequisites.sh
│ └── powershell/ the same three, as .ps1
└── presets/
specs/001-your-feature/ feature level, written per feature
├── spec.md what and why, no tech choices
├── plan.md how, grounded in the constitution
├── tasks.md ordered work, dependencies marked
├── data-model.md
├── contracts/
└── research.md
The pipeline runs in one direction. /speckit.constitution sets the principles.
/speckit.specify writes the requirements. /speckit.plan picks the stack, and it
refuses to guess when the constitution already named one. /speckit.tasks breaks the plan into
units small enough to review. /speckit.implement executes them.
Two commands sit between them and are optional, which is exactly why people skip them.
/speckit.clarify asks you up to five targeted questions about the parts of the spec that are
ambiguous, and writes your answers back into the file. /speckit.analyze reads spec, plan and
tasks together and reports contradictions, unmeasurable requirements and requirements no task covers. It
changes nothing. It only tells you.
Installing it takes one line, and the version tag in that line matters more than it looks:
uv tool install specify-cli --from git+https://github.com/github/spec-kit.git@v0.9.5
specify --version
specify init my-project --integration claude
Pin the tag. On releases before v1.0, leaving it off meant init had to ask the API what the latest release was, and that question is the one that failed. Pinning also keeps two projects on the same scaffolding, which matters more once a team is involved.
Ordered roughly by how often they turn up. Every error string below is reproduced as it appeared.
Error fetching release information GitHub API returned 403 for
https://api.github.com/repos/github/spec-kit/releases/latest
Anonymous calls to that endpoint are capped per hour, and shared office or CI addresses burn through the cap quickly. Nothing is wrong with your install. Pass a personal access token, written out, on the command line:
specify init . --ai claude --github-token=ghp_your_real_token
The failure mode people hit next is subtle. Writing --github-token GITHUB_TOKEN passes the
eight characters GITHUB_TOKEN. The variable is not expanded, the token is never read, and the
error does not change. Use the --github-token= form with the actual value.
401 Unauthorized: GitHub API returned status 401 for
https://api.github.com/repos/github/spec-kit/releases/latest
This reads like the previous fix failed and it is not the same problem. If GITHUB_TOKEN or
GH_TOKEN is set to something expired, the tool still sends it, and GitHub rejects a bad token
where it would have served an anonymous request. The counterintuitive move is to remove the token you just
added and try again with nothing:
# PowerShell
$env:GITHUB_TOKEN = $null
$env:GH_TOKEN = $null
specify init . --ai claude
# bash
unset GITHUB_TOKEN GH_TOKEN
specify init . --ai claude
If anonymous works, leave it alone. If it does not, put a freshly generated token back, inline. Do not
put it in .env and expect it to be read.
No module named 'specify'
The package resolved and the entry point did not. Older uv releases handle this install
badly, and the error points at Python when the actual culprit is the installer. One command:
uv self update
uv tool install specify-cli --from git+https://github.com/github/spec-kit.git
Then confirm with specify --version. It should print something shaped like
specify-cli 0.9.5. If your shell says the command does not exist, the install is fine and
your PATH is a session behind. Open a new terminal.
You install Python 3.14.2, run python --version, and get 3.10.11. The old
paths sit ahead of the new ones in Path, and priority is positional. Move the 3.14 entries
to the top, then refresh the current session instead of rebooting:
$env:PATH = [Environment]::ExpandEnvironmentVariables(
[Environment]::GetEnvironmentVariable("PATH", "User") + ";" +
[Environment]::GetEnvironmentVariable("PATH", "Machine"))
That applies to the window you are in. Other open windows still hold the old value, which is worth remembering before you conclude the fix did not work.
On the same platform, once the tool actually runs, the console itself can be the next problem:
UnicodeEncodeError: 'gbk' codec can't encode character '\u2022'
The code page is GBK and the message contains a bullet. Two ways out, either is enough:
chcp 65001
$env:PYTHONIOENCODING = "utf-8"
A proxy gets you part of the way:
$env:HTTP_PROXY = "http://127.0.0.1:7890"
$env:HTTPS_PROXY = "http://127.0.0.1:7890"
specify init . --ai claude
If the proxy terminates TLS and the handshake fails, --skip-tls gets you through. If there
is no route at all, stop trying to reach the API and build the template bundle yourself. Clone the
repository, let the release script build the directory, then package it. The script calls zip,
which Windows does not ship, so it dies at the last step and leaves the directory behind:
bash .github/workflows/scripts/create-release-packages.sh v0.3.2
python -c "import zipfile; from pathlib import Path; \
base = Path('.genreleases/sdd-claude-package-sh'); \
z = zipfile.ZipFile('.genreleases/spec-kit-template-claude-ps-v0.3.2.zip','w',zipfile.ZIP_DEFLATED); \
[z.write(f, f.relative_to(base)) for f in base.rglob('*') if f.is_file()]; z.close()"
Then point init at the file you just made rather than at the network:
specify init . --ai claude \
--local-template .genreleases/spec-kit-template-claude-ps-v0.3.2.zip \
--here --force
One more that catches people before any of this: if you name an agent that is not installed, init
stops at the selection step. specify init . --ai claude --ignore-agent-tools skips the check
and writes the files anyway. On current releases that flag is spelled
--integration claude, and --ai is the older name for it.
Worth knowing before you copy any of the above, including the parts of this page. From v1.0 onwards the
templates ship inside the wheel itself, so specify init no longer resolves a release over the
API and works without network access. The GitHub download path was retired rather than fixed.
Two consequences follow. The 401 and 403 failures above belong to v0.x, and if you are on v1.0 or later and still seeing them, your CLI is out of date rather than your network being blocked. And the local template workaround is no longer the recommended route, because there is nothing left to download.
One wrinkle to be aware of if you do go looking for that workaround: some write-ups pass a
--local-template flag. The released CLI does not expose it. The supported spelling is
--template-path, which takes a zip or a directory, and the matching environment variable is
SPEC_KIT_TEMPLATE_PATH.
All ten failures, including the ones that did not fit above. Symptom, the exact text, the environment, the cause, the fix, and the date it was first written up.
The file lives at
https://openspec-mvp.pages.dev/spec-kit-init-failures.csv,
served as text/csv, ten rows plus a header. It opens in Excel, Numbers or any text editor.
| Symptom | Environment | Cause | Fix | Written up |
|---|---|---|---|---|
| Init stops, nothing written | Windows PowerShell | API rate limit on releases | --github-token=ghp_... | 2025-12-10 |
| 401 instead of 403 | token present in environment | expired token still sent | clear both vars, go anonymous | 2026-03-20 |
Token from .env ignored | any shell | variable name passed literally | --github-token= with the value | 2026-03-20 |
No module named 'specify' | macOS, older uv | entry point not resolved | uv self update, reinstall | 2026-07-14 |
| Reports the older Python | two interpreters on Path | old entries rank higher | move new paths to the top | 2025-12-13 |
UnicodeEncodeError on a bullet | GBK console | character not encodable | chcp 65001 | 2026-03-20 |
| Fetch stalls, no error | behind a proxy | outbound HTTPS blocked | set proxy vars, --skip-tls | 2026-03-20 |
| Same 401 or 403 forever | no route to GitHub | init resolves latest over API | local template zip | 2026-03-20 |
zip: command not found | Windows release script | script shells out to zip | zip with Python zipfile | 2026-03-20 |
| Stops at agent selection | agent not installed | init verifies the agent | --ignore-agent-tools | 2025-12-10 |
Every figure is claimed by a specific write-up on a specific date. None of it is a benchmark and none of it transfers to your machine by default. Read it as other people's logs.
| Date | Who reported it | What they reported |
|---|---|---|
| 2025-12-10 | A hands on walkthrough running init inside an existing repository | Error fetching release information GitHub API returned 403 for
https://api.github.com/repos/github/spec-kit/releases/latest. Resolved by generating a personal
access token and passing it inline. No counts reported. |
| 2025-12-13 | A Windows install walkthrough with a pasted PowerShell session | python --version printed 3.10.11 while 3.14.2 was installed. Installed
uv 0.9.17 from a mirror, a 22.1 MB wheel at 49.9 MB/s. Fix was reordering the user Path
and refreshing it in session. |
| 2026-03-20 | A troubleshooting write-up aimed at blocked networks | 401 Unauthorized on the same releases URL, and
UnicodeEncodeError: 'gbk' codec can't encode character '\u2022'. Templates built at
v0.3.2, packaged with Python because zip is absent on Windows. |
| 2026-06-08 | Repository counters quoted in a workflow walkthrough | 110,000 stars and 9,700 forks, with 105 community extensions and 22 presets. |
| 2026-07-14 | A macOS install walkthrough | No module named 'specify' on an older uv, fixed with
uv self update. specify --version printed specify-cli 0.9.5.
Floor is Python 3.11. |
Most write-ups stop at the install instructions. This is the part they leave out.
plan.md before implement runs. Skip that review and you have moved the guess
from the code into a document, which is worse, because now the guess is signed off.Six lines, in order. If one of them fails, stop there, because the errors downstream are all downstream of it.
python --version # 3.11 or newer
uv --version # then uv self update
specify --version # prints the tag you pinned
$env:GITHUB_TOKEN = $null # clear a stale token first
$env:GH_TOKEN = $null
specify check # reports what it can see
After init succeeds, restart your editor. The command files are on disk and the editor has not loaded them, so the slash commands look missing until it reloads.
Init is idempotent. Run it again and it refreshes the scripts and templates, applies a new preset, or
switches the integration you are working with. It also keeps a hash of every file it wrote, which is how it
tells your edits apart from its own. Files you changed are left alone unless you pass --force.
That makes upgrades safer than most scaffolding tools manage, and it makes them quiet, which is the
problem. A release can rename a flag, as v1.0 did when --ai became --integration,
or change what init puts on disk, and nothing will fail while it happens. Pin the tag in your install
command. After any upgrade, look at what changed under .specify/ before you start the next
feature.
Everything after that is dull in a good way. Write the principles once, describe the feature, let the tool ask you the questions you would have skipped, read the plan while there is no code yet, and let the agent work the list.