Search Docs
Loading...
Skip to content

Troubleshooting

Find the symptom you see when you start, run, or roll out the CoDesign SDK, 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-<kit>, for example codesign-executor-ai-sdk:

Terminal window
docker logs codesign-executor-<kit>

A 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_LICENSE is 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 with the 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.ly and exits when that fails. The log says how: could not reach the license server https://api.img.ly/activate: … (a network rule blocks api.img.ly, or an outbound proxy is set without NODE_USE_ENV_PROXY=1 in the container’s environment), the license server answered <status>: …, or the license is <status> (the key is not valid).
  • EXECUTOR_TOKEN is missing. Set it to a secret you make yourself, such as openssl 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: acquire answered undefined, 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/health answers, and docker logs with the container’s name says why it is not.
  • executor_unreachable in the middle of a run: the executor that held the conversation exited or restarted, so the calls in flight got no answer. Read its log from before the restart, and check whether it ran out of memory: docker inspect <container> --format '{{.State.OOMKilled}}' prints true then (in a quickstart, the container is codesign-executor-<kit>). Give each executor 2 GB of memory and one vCPU, on a host that has them free ( Run Enough Executors). The next call opens the design on the executor acquire answers, from the last version your design store has.
  • executor_capacity: a design opened while its executor was exiting, and the executor was still exiting after 60 seconds. Retry; acquire then 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.

The Gateway Does Not Answer#

A model call fails with Cannot connect to API, Connect Timeout Error, fetch failed, ETIMEDOUT, or EAI_AGAIN: your agent runner did not reach IMG.LY’s AI gateway. Most agent frameworks retry a model call a few times, then fail the run with the last error. Check from the runner’s machine:

Terminal window
curl -s -o /dev/null -w '%{http_code}\n' https://gateway.img.ly/v1/models

401 means the gateway answers (the list needs your key). A timeout or a DNS error means the runner’s network does not reach gateway.img.ly: allow it in the runner’s egress and proxy, and check its DNS. Then run again.

The gateway charges each model call it answers. It does not know about your run, so the calls before a failure are charged, and a run you start again pays for its calls again. Keep what the model made: save the design before you export, as the quickstart kits do with .imgly.

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: generateText throws 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_refused and 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 export is a PDF the executor cannot count: one that stores its objects compressed, or was updated in place.
  • 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.
  • The message ends in “update the SDK”: the export request came from a runner whose SDK sends no render id, or one Metering does not accept. The render id is 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, either because its egress (outbound network access) blocks api.img.ly or because the network failed during that first export. Allow api.img.ly in its egress. If it is allowed, retry the export: a retried export of the same design is charged once.
  • Your uploadExport returned neither downloadURL nor note.

Retrying unchanged helps only for the export limit, which lifts with time, and for a network failure on the billing route. For the rest, fix the cause first: top up the account, use a CoDesign license key, export smaller or in another format, update the SDK, open the egress, or return a link or a note from uploadExport. 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, toModelOutput hands 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, as imagesAfterToolResults does in the Vercel AI SDK quickstart. pi, the agent library from the pi quickstart, moves them itself when the model entry lists input: ['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 Model Asks a Question Instead of Designing#

The run ends with a question from the model, such as which colors to use, and no design. Nothing fails: the model waited for an answer that nobody in the run could give. It happens with a short brief in a run where nobody can answer the model’s questions, such as a design API, a batch, or a quickstart. Add this line to the task you send with the brief: “Design this and reply once it is done. No one can answer questions during this run.” Or write a fuller brief that leaves the model nothing to ask.

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 finishReason but '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#