OpenCode and Ollama: Why num_ctx Is 4096, and What a Local Coding Agent Actually Costs
Your model says it holds 40960 tokens. Ollama gives it 4096, worked out from your video memory, and that is why a local coding agent forgets the file it just read. Tested on an M4 Pro: how to raise num_ctx and what it costs, why a qwen3 request can return nothing while still burning tokens, and the real timing of a trivial task. Two minutes thirty four.
📅
OpenCode and Ollama: Why num_ctx Is 4096, and What a Local Coding Agent Actually Costs
In February we published a setup guide for running OpenCode against a local Ollama instance. Eight months later OpenCode has gone from 0.5 to 1.15, a major version jump, and the obvious thing to check was whether that guide still works. It does. The configuration file written in February is read without complaint by OpenCode 1.15.6, and all four local models show up.
That is the boring half of this article. The interesting half is everything the setup guide does not tell you, because it stops the moment the thing works. What nobody writes down is why your agent forgets the file it just read, why a request can come back completely empty while still burning tokens, and how long a trivial task really takes on a laptop. All three have the same root: local inference has a budget, and the defaults spend it for you.
What was tested, and on what
Apple M4 Pro, 24 GB unified memory, macOS 26.6.2. Ollama 0.15.6, OpenCode 1.15.6, models qwen3:14b and a custom qwen3:14b-32k variant. Every number below comes from that machine, including the slow ones.
The model says 40960. Ollama gives you 4096
This is the single most useful thing in this article, and it explains most of the confusion around local coding agents.
Ask Ollama what your model supports and it answers honestly:
ollama show qwen3:14b | grep -i "context length" # context length 40960
Forty thousand tokens. Plenty for a coding agent. Except that number is the model's architectural ceiling, not what you get. Start the Ollama server and read its own startup log:
msg="vram-based default context" total_vram="17.8 GiB" default_num_ctx=4096
Ollama looks at the video memory it can actually use, 17.8 GiB of the 24 GB on this machine, and decides that 4096 tokens is what it will allocate. Not 40960. The model could hold ten times more; the runtime chose not to.
Four thousand tokens is roughly one medium source file plus the conversation around it. A coding agent reads a file, runs a command, reads the output, and that budget is gone. The symptom is not an error message. The agent simply loses the earlier part of the conversation and starts answering as if it had never seen your code. People reasonably conclude the model is bad. The model is fine and was never given room to work.
Two numbers, two different meanings
context length is what the model architecture supports. num_ctx is what Ollama allocates for this run. Only the second one affects you, and by default it is derived from your available video memory rather than from the model. Checking the first and assuming the second is the mistake.
Raising num_ctx, and what it costs you
There are three ways to give the agent more room, and they differ in how permanent they are.
The first is per request, useful for testing but ignored by most tools since they do not expose it:
curl http://localhost:11434/api/generate -d '{
"model": "qwen3:14b",
"prompt": "hi",
"options": { "num_ctx": 16384 }
}'The second is global, applied to every model on the machine:
# macOS, for the Ollama app launchctl setenv OLLAMA_CONTEXT_LENGTH 32768 # or when running the server yourself OLLAMA_CONTEXT_LENGTH=32768 ollama serve
The third, and the one worth the trouble, is a model variant that carries its own context setting. Create a Modelfile, build it once, and every tool that asks for that model name gets the larger window without knowing anything about it:
# Modelfile FROM qwen3:14b PARAMETER num_ctx 32768 ollama create qwen3:14b-32k -f Modelfile ollama show qwen3:14b-32k | grep num_ctx # num_ctx 32768
This is the version worth having, because the setting travels with the model name. Point OpenCode at qwen3:14b-32k and the context is right whatever launched it.
The cost is memory, and it is not small. Context is allocated up front as a key value cache, so eight times the window means a large block of video memory reserved before a single token is generated. On a 24 GB machine running a 14B model that is affordable. Trying the same with a 30B model on the same hardware is where you start trading context against the model staying fully on the GPU, and a model that spills to CPU is not slightly slower, it is unusable for interactive work.
The empty answer that still costs tokens
This one cost me twenty minutes of confusion, which is a good sign it is worth writing down.
A plain request through the OpenAI compatible endpoint, asking for a four word answer, with a sensible cap on output:
curl http://localhost:11434/v1/chat/completions -d '{
"model": "qwen3:14b-32k",
"messages": [{ "role": "user", "content": "Reply with exactly: config works" }],
"max_tokens": 40
}' The response came back with HTTP 200, an empty content field, forty completion tokens consumed, and finish_reason: "length". Nothing was returned and the budget was spent.
The same request with the cap raised to 600 answers correctly: config works, twelve characters, ninety six completion tokens, finish_reason: "stop".
Twelve characters cost ninety six tokens because qwen3 is a reasoning model. It thinks first, and the thinking is counted. Ollama does not hide it: alongside content the response carries a reasoning field, and in the failed request that field held a half finished thought that was cut off before the model reached its actual answer. Around eighty four tokens of deliberation for a four word reply.
This matters beyond curiosity. February's guide recommended qwen2.5-coder, which does not reason and does not do this. If you have since moved to qwen3, as most people have, every output limit you set needs to accommodate the thinking as well as the answer. A limit that was generous before is now a limit that returns nothing while charging you for it.
If your local agent returns empty responses
Check finish_reason before anything else. If it reads length with empty content, you are not looking at a broken model or a broken configuration: the output cap is being consumed by reasoning tokens. Raise the cap, or use a non reasoning model for short structured tasks.
What it actually costs: two minutes thirty four
Benchmarks of local models usually report tokens per second, which tells you almost nothing about using one. Here is a complete, ordinary task instead.
A two line Python file with an obvious bug, and one question:
def add(a, b):
return a - b
opencode run --model ollama/qwen3:14b-32k \
"Read calc.py and tell me in one sentence what bug it has."It worked, and correctly. The agent searched for the file, read it, and answered that the function subtracts instead of adding. The full agent loop, tool calls included, runs against a local model exactly as advertised.
It took two minutes and thirty four seconds. To read two lines and write one sentence.
That is the number to sit with before planning your workflow around local inference. It is not a criticism of the tools, which did what they promised on hardware that costs a fraction of a GPU server. It is the actual shape of the trade. A hosted model answers that question in about two seconds. Local costs you roughly seventy times longer, and buys you code that never leaves the machine and a bill that never arrives.
So when is this worth it
The honest answer is narrower than the enthusiasm around local AI suggests, and wider than the sceptics allow.
Where the trade works
Code you are not allowed to send anywhere. Client work under NDA, regulated environments, anything where the compliance conversation costs more than the waiting. Here two minutes is cheap.
Batch work you are not watching. Documentation passes, repetitive refactors, anything you can start and come back to. Latency stops mattering when nobody is waiting.
Learning how agents actually behave. Running one locally shows you the tool calls, the context limits and the token cost in a way a hosted endpoint hides behind speed.
Where it does not work is the interactive loop: asking, reading, adjusting, asking again. Two and a half minutes per turn breaks the rhythm that makes an agent useful in the first place.
The part that aged well
Worth ending on, because it is unusual. A configuration file written in February, for a tool that has since crossed a major version boundary, still works unchanged in October. Same schema, same npm package, same base URL with its mandatory /v1, same "tools": true. OpenCode 1.15.6 reads it and lists every model.
In a part of the industry that rewrites its interfaces every quarter, a config that survives eight months and a major release is worth noticing. The setup guide was not what needed updating. What needed writing was everything that happens after the setup works.
Sources and Further Reading
Every figure here was measured on the machine described above on 9 October 2026, not taken from documentation. The installation path, the Modelfile technique and the troubleshooting steps are in our Ollama and OpenCode setup guide. Context handling and the num_ctx parameter are documented in the Ollama Modelfile reference, and the OpenAI compatible endpoint in the Ollama OpenAI compatibility notes. OpenCode's provider configuration is described in the OpenCode documentation.
Published October 2026. This is an opinion piece and analysis, not a sponsored post. CodeHelper has no commercial relationship with the companies mentioned.
Frequently Asked Questions
Why does Ollama use 4096 context when my model supports 40960?
Context length is the model architecture ceiling; num_ctx is what Ollama allocates for the run. Recent versions derive that default from available video memory rather than from the model. On a 24 GB Mac with 17.8 GiB usable, the server log reports default_num_ctx=4096. The model could hold ten times more, and the runtime chose not to allocate it.
How do I increase num_ctx for OpenCode and Ollama?
Three ways. Per request through the options object of the API, which most tools do not expose. Globally with the OLLAMA_CONTEXT_LENGTH environment variable, set with launchctl setenv on macOS or in front of ollama serve. Or, best for a coding agent, a model variant built from a Modelfile carrying PARAMETER num_ctx 32768, because the setting then travels with the model name whatever launches it. The cost is video memory reserved up front for the key value cache.
Why does my qwen3 request return an empty response?
Check finish_reason. If it reads length with empty content, the output cap was consumed by reasoning tokens before the model reached its answer. qwen3 thinks first and Ollama returns that thinking in a separate reasoning field, counted in completion_tokens. In testing, a four word reply used 96 completion tokens; the same request capped at 40 returned nothing and spent all 40. Raise the cap, or use a non reasoning model for short structured tasks.
How fast is a local coding agent in practice?
On an M4 Pro with 24 GB running qwen3:14b with a 32k context, OpenCode took two minutes and thirty four seconds to read a two line Python file and describe its bug in one sentence, tool calls included. The loop works exactly as advertised; a hosted model answers the same question in about two seconds. Local makes sense for code that cannot leave the machine and for batch work nobody is waiting on, not for an interactive ask and adjust loop.