Find the symptom you see when you start, run, or roll out CoDesign, and the change that fixes it. Some problems stop with an error; others fail silently: the run finishes, but the design is poor.
Calls that fail for a reason the model cannot fix reject with an error whose
kind names the cause; isCoDesignError(err) checks for one, and the
error reference lists every kind. Terms such as
executor and agent runner are in Terminology.
Container Does Not Start#
The executor checks its setup at start and exits when something is wrong. A
container that exits at once, or a /health call that gets no answer, has the
reason in its log. In the quickstarts, the container is named
codesign-executor:
docker logs codesign-executorA healthy container answers curl http://localhost:8080/health with status
200 and "accepting": true. On Kubernetes, kubectl logs <pod> shows the
same log, and kubectl describe pod <pod> shows what Kubernetes itself
refused, such as OOMKilled for a container over its memory limit.
The common causes:
IMGLY_CESDK_LICENSEis missing or is not a key. Set it to your CoDesign license key, the short string itself. Contact sales to get a CoDesign license key. A CE.SDK license file or a grant (the short-lived license IMG.LY issues for one engine) is refused withthe billed executor needs a CoDesign license key, not a license file or grant.- The key cannot be activated. At start, the executor activates the key at
api.img.lyand exits when that fails. The log says how:could not reach the license server https://api.img.ly/activate: …(a network rule blocksapi.img.ly, or an outbound proxy is set withoutNODE_USE_ENV_PROXY=1in the container’s environment),the license server answered <status>: …, orthe license is <status>(the key is not valid). EXECUTOR_TOKENis missing. Set it to a secret you make yourself, such asopenssl rand -hex 32.
The image also refuses to start as root, with a debugger, under an init program, or with a setting it does not take; the log names which. For the flags and settings to run it with, see Run the Image Hardened.
401 From the Executor#
The executor answers 401 with missing or wrong executor token when the agent
runner sends a different bearer token from the one the container was started
with. The call rejects with the error kind executor_unauthorized. Pass the
container’s EXECUTOR_TOKEN as token to remoteExecutors. In
the quickstarts, both read it from the kit’s .env: after changing it, run
npm run executor:stop and npm run executor so the container gets the new
value, then npm start; the run then gets past its first tool call.
GET /health needs no token, so a working /health does not prove the token
matches.
Runner and Executor on Different Versions#
The call rejects with the error kind executor_version, and the executor
answers HTTP 409. Its message names both sides’ protocol (the version of the
messages they exchange) and your runner’s SDK_VERSION. Run
imgly/codesign-executor tagged with the exact SDK version your runner
installed (npm ls @imgly/codesign-sdk shows it). Two SDK builds on the same
protocol are not refused, so pin both
( Versions and Upgrades).
The Session Ends With executor_changed#
The call rejects with executor_changed to your code, not to the model: the
conversation’s executor was lost, and the one acquire answered next runs
another CE.SDK version, can do other things, or registers other asset sources.
Call again, which opens the design on the new executor. You see it during a
rollout with old and new executors in one pool; replace the old containers
after a deploy
( A New Container for Each Conversation).
A Call Fails With executor_unreachable or executor_capacity#
executor_unreachable:acquireansweredundefined, or the executor gave no answer (or a proxy in front of it answered 502 or 504). Run more executors, or retry the turn later. On your own machine, the container may still be starting, or restarting after the previous run: once it is up,curl http://localhost:8080/healthanswers, anddocker logs codesign-executorsays why it is not.executor_capacity: a design opened while its executor was exiting, and the executor was still exiting after 60 seconds. Retry;acquirethen answers a new executor for the conversation ( Find a Conversation’s Executor).
A call that ran past 5 minutes, usually model code in an endless loop, is not
one of these. The model reads an error and can try again, and only your own
create(), load(), edit(), or export() calls reject, with
executor_failed. The executor then exits, and the next call loads the
design on a new executor.
402 From the Gateway#
IMG.LY’s AI gateway answers 402 insufficient_credits when the IMG.LY account
behind the key is out of credits. Top up the account, then run again.
How the run reports it depends on the agent framework:
- Vercel AI SDK:
generateTextthrows with the gateway’s error. - pi: the run ends without throwing, and the error is in the last message
(
stopReason: 'error'). Check it before you export, or you export a design the model never finished. - Image generation: the tool call rejects with the error kind
generation_refusedand a message for the person. Show it, or offer a top-up. Retrying does not help until the account has credits.
Exports Are Refused#
An export that the executor does not charge fails with the error kind
export_refused. Its message says why, in words you can show the person:
- The account has no credits left.
- The license key does not include CoDesign. A plain CE.SDK key starts the engine, but every export is refused. Contact sales to get a CoDesign license key.
- The export is over a limit: more than 16,000 px on its longer side, 500 pages, or 150 MB.
- The format is video or audio, which the executor does not export.
- Metering (IMG.LY’s billing service) answered 429 without
Retry-After: “This account has reached its export limit for now.” A limit such as exports per user per day refuses this way. - “This export carries no render id; update the SDK.”: the export request came from a runner whose SDK does not send a render id, the id that makes a retried export charge once. Update the runner’s SDK.
- “The billing route https://api.img.ly/exports/claim could not be reached,
so this export was not delivered.”: the container has not reached Metering
once since it started. Allow
api.img.lyin its egress (outbound network access).
Retrying unchanged does not help, except for the export limit, which lifts with time. Fix the cause first: top up the account, use a CoDesign key, export smaller or in another format, update the SDK, or open the egress. For what the image charges and when it refuses an export, see Billing.
Another Conversation Is Refused#
An executor serves only the first conversation that opens a design on it. A
call for any other conversation fails with the error kind
executor_bound. Your acquire handed out an executor that another
conversation already used. Give each new conversation an unused executor, and
the same one while the conversation lasts. The image remembers which
conversation it serves in a file on a mounted volume, so a container restarted
with that volume stays bound. The quickstarts keep it in memory instead: about
30 seconds after a run releases its design, the container exits and comes back
unbound. If you change the
conversation between runs, wait for that restart before the next run.
The Model Designs Without Seeing Its Work#
Edits and previews answer with renders, and the model needs to see them as images. When it gets none, nothing fails: the model works blind and cannot fix what it cannot see. Check these:
- Every part of a tool result reaches the model, images included. With the
Vercel AI SDK,
toModelOutputhands the renders over as files. Without it, the model reads each render as a long text string. - The images sit where your provider takes them. A Chat Completions API,
IMG.LY’s gateway included, takes no images inside a tool result. With the
Vercel AI SDK, move them into a user message after the result in
prepareStep, asimagesAfterToolResultsdoes in the Vercel AI SDK quickstart. pi, the agent library from the pi quickstart, moves them itself when the model entry listsinput: ['text', 'image']. - The model accepts images. Pick a model that reads images.
The Model Ignores the Design Rules#
End your system prompt with codesign.instructions. They tell the model to
read the handbook, IMG.LY’s design rules that the SDK serves to the model,
before its first edit, to look at the design after every
change, and to judge it before it says it is done. Left out, nothing fails: the
model may skip the handbook and finish a design it never checked. With pi,
pass no convertToLlm filter that drops system messages: pi sends your system
prompt and the tool list together in one system message, so a filter that
drops it removes both.
The Run Stops Early#
A run can end before the model is done:
- A step limit. By default, the Vercel AI SDK ends a run after the first
tool call.
stopWhen: isStepCount(60)lets the model take up to 60 steps. A run that reaches the limit ends with the design half done. - The model’s output limit. A reply cut off at the model’s output limit
ends the loop with its tool calls not run, and looks like a finished run.
With the Vercel AI SDK, treat any
finishReasonbut'stop'as a failed run. - An error. The gateway or the executor refused a call; see the sections above.
The quickstart scripts export only when the run ended normally, so a run that
stops early exports nothing and costs no export. With a designs store, every
edit that succeeded is already saved in your storage, so you can continue the
design in a new run ( Connect Your Storage).
Next Steps#
- Host the Executor — run the image hardened, with a new container for each conversation.
- Versions and Upgrades — keep the runner, the executor, and your editor on matching versions.
- Errors — look up every error kind your code can receive.
- Connect Your Storage — keep designs between runs.