Troubleshooting functions
Each heading below is a real symptom, quoting the exact error message where there is one. Under it you get the cause and the fix. Search this page for the text of the error you are seeing.
Deploy errors
this project cannot deploy yet: ... no Crusoe Cloud credential is mapped to this project
Symptom: the very first deploy into a project fails with status 409, before any build output exists:
this project cannot deploy yet: its container images are built into your own Crusoe Cloud container registry, and no Crusoe Cloud credential is mapped to this project. A project admin sets one with PUT /v1/projects/{id}/crusoe-cloud {"access_key_id":"...","secret_key":"..."} (in the console: Project Settings), then deploy again. The repository itself is created for you on the first deploy - there is nothing to pre-create. Nothing was built and your stored source was not touched.
Cause: your function is built into a container image, and that image is stored in a repository in your own Crusoe Cloud Registry. The project has no Crusoe Cloud credential saved, so there is nowhere to push it. The refusal happens at submit, which is why there is nothing in the logs to read.
Fix: a project admin connects the project once — Project Settings in the console, or platformctl crusoe-cloud connect --access-key-id <id> with the secret key on standard input — and then you deploy again. Nothing was built, so there is no partial state to clean up, and a project that is already connected never sees this. Walkthrough: connect your Crusoe Cloud account.
unsupported 'framework': one of "adk", "langgraph", "crewai", "function", "container"
Symptom: a POST /v1/agents request fails with status 400 and this message.
Cause: functions deploy through the agents endpoint, and the framework form field must be exactly function. Any other value is rejected, including a typo like functions or an unknown framework name. One common mistake is sending the platform's internal build token as the framework: function-nodejs, function-go, or function-ruby. The server produces those itself and does not accept them as input.
Fix: set framework=function in the multipart form, and select a non-Python language with the separate runtime field (nodejs, go, ruby). platformctl functions deploy does this for you, so this error should not appear from the CLI.
It did until August 2026. Back then the CLI folded the runtime into framework, and every non-Python deploy hit exactly this check. So if you see this error from the CLI, your platformctl predates that fix — rebuild it. See runtimes for the full command.
'runtime' only applies to framework=function
Symptom: a raw POST /v1/agents request fails with status 400 and this message.
Cause: you sent a runtime field together with an agent framework (adk, langgraph, or crewai). The runtime field picks a function's language, and agents do not have one. The platform rejects the combination rather than ignoring the field without telling you.
Fix: if you meant to deploy a function, change framework to function. If you meant to deploy an agent, drop the runtime field.
unsupported 'runtime': one of "python", "nodejs", "go", "ruby"
Symptom: a raw POST /v1/agents request that carries framework=function fails with status 400 and this message.
Cause: the runtime value is not one of the four supported ones. A CLI caller reaches this check too. platformctl functions deploy --runtime <value> passes an unrecognized value straight through in the runtime field, on purpose, so a runtime added to the platform works before the CLI knows its name. That is why --runtime node or --runtime golang produces this server error rather than a client-side one.
Fix: use python, nodejs, go, or ruby.
missing or invalid 'name' (must be a lowercase DNS label)
Symptom: the deploy fails with status 400 and this message.
Cause: function names, like agent names, must be lowercase DNS labels. A DNS label is a single piece of a hostname, so the rules are strict. Use lowercase letters, digits, and dashes only. It must start with a letter, end with a letter or digit, and be at most 63 characters. My_Function fails on the capitals and the underscore. 2nd-try fails because it starts with a digit.
Fix: rename it to something like my-function or second-try. Two related things to know. Agents and functions share one pool of names per project, so a name an agent already uses is not available for a function. And commands that address an existing function by name (status, invoke, logs, delete) reject a bad name with a different message for the same reason: invalid agent name (must be a lowercase DNS label).
failed to build/deploy (see platformctl logs ...)
Symptom: platformctl functions deploy ends with:
hello-fn failed to build/deploy (see `platformctl logs hello-fn`)
Cause: the platform accepted your upload, then the build or the rollout failed. The usual reason is a dependency that would not install, from requirements.txt, package.json, or Gemfile. For Go it is usually a compile error. One Go-specific build failure is worth knowing:
go function requires a handler.go with a Handle(event) func
which means you deployed with --runtime go but the directory has no handler.go.
Fix: run platformctl logs hello-fn to read the failure, correct the code or dependency file, and deploy again. Each deploy replaces the whole source tree, so there is no partial state to clean up.
timed out after 5m0s waiting for hello-fn (last state: building)
Symptom: the CLI gives up after checking the state for five minutes.
Cause: the build is slow, usually because it is installing large dependencies, or the platform is busy. The build has not necessarily failed. The CLI just stopped waiting for it.
Fix: check again with platformctl status hello-fn. If its state reaches ready, nothing is wrong. If it sits in building forever or flips to failed, read platformctl logs hello-fn and trim your dependencies.
The upload is rejected as too large, or the console refuses my files
Symptom: a CLI deploy fails on upload, or the web console refuses the directory.
Cause: the CLI uploads your entire directory, up to a cap of 100 MiB. There is no way to tell it to ignore files. A stray .venv/, node_modules/, or .git/ directory is the usual culprit.
The console is stricter still. It caps archives at 32 MiB, accepts text files only, and limits each file to 1 MiB. When you hit any of those, its error message tells you to use platformctl instead.
Fix: deploy from a clean directory holding only your handler and its dependency file. Use platformctl functions deploy for anything the console will not upload.
Wrong behavior after deploy
My Python function is detected as another language
Symptom: you wrote a Python function, but the CLI prints runtime=nodejs (or go, ruby) while uploading, and the deployed function behaves like the wrong language.
Cause: the CLI auto-detects the runtime from file names — handler.js means nodejs, handler.go means go, handler.rb means ruby, anything else means python. A leftover handler file from another language wins the detection, and nothing downstream questions it.
Fix: remove the stray handler file, or name the language yourself with --runtime python. The choice is remembered: redeploys keep the same runtime automatically. The upload line names the runtime it picked, so it is worth reading before the build starts.
The console has no Redeploy button on my function
Symptom: the redeploy shortcut you see on an agent's page is missing on a function's page.
Cause: the console's Functions page leaves the button out on purpose and offers "Update source" instead. The endpoint behind it, POST /v1/agents/{name}/redeploy, is not blocked for functions. It rebuilds from the source the platform already has, using the framework recorded on the deployment. So a function rebuilds as a function, in its original runtime.
Fix: to ship a new version of a function, run platformctl functions deploy ./my-dir --name my-function again, or use the console's "Update source" action on the function's page. Either path rebuilds the function in its original runtime.
Runtime errors
{"error": "handler raised: ..."}
Symptom: on a plain HTTP call, the caller gets a 500 with this body. On a CloudEvent delivery, the event system logs a 400 with the same body.
Cause: your handler threw an exception. The platform catches it and sends back a real HTTP response instead of dropping the connection. For CloudEvents it uses 400 on purpose, because that tells the event system to stop redelivering an event that will fail the same way every time. See HTTP and events.
Fix: the text after handler raised: is the exception message. The full stack trace is in the function's logs: platformctl logs my-function --history.
{"error": "body exceeds 8388608 bytes"}
Symptom: a POST to the function returns status 413 with this body. On the Go runtime the body reads {"error": "body too large or unreadable"} instead.
Cause: the request body is over the 8 MiB event cap. The handler never ran.
Fix: send less data per request. For anything large, send a reference to it instead of the data itself — an object-storage location, or a database ID your handler can look up.
Logs and authentication
platformctl logs shows nothing, or the function has no running instance
Symptom: platformctl logs my-function returns nothing useful for an idle function, or --history prints no persisted logs.
Cause: an idle function has scaled to zero, so there is no live process to read from. Plain logs follows the live copy. --history reads saved lines, which survive scale-to-zero. no persisted logs means the function has not logged anything yet.
Fix: run platformctl logs my-function --history after the function has served at least one request. Do not combine the two flags:
--follow and --history are mutually exclusive: --history reads persisted logs, --follow tails a running instance
this endpoint requires authentication
Symptom: deploy, status, logs, or delete fails with a 401 carrying this message:
this endpoint requires authentication. Sign in (POST /v1/auth/login) and send 'Authorization: Bearer <token>', or create an API key in the console under Settings -> API keys
From the CLI, one more clause is appended, naming the credential platformctl actually sent:
[platformctl credential: none; run `platformctl login` or set $CAI_TOKEN]
Cause: every management route needs a credential, and yours is missing or expired. Sign-in sessions last 12 hours. none in that suffix means the CLI found no credential at all; any other value names the source it used, which is how you tell "not signed in" from "signed in as the wrong account".
Fix: run platformctl login again, or set $CAI_TOKEN to an API key. See API authentication and service accounts and API keys.
this platform requires authentication to invoke agents (INVOKE_AUTH_REQUIRED=true)
Symptom: calling the function returns a 401 with this body, from a script that used to work without a credential:
{"error": "this platform requires authentication to invoke agents (INVOKE_AUTH_REQUIRED=true). Sign in (POST /v1/auth/login) and send 'Authorization: Bearer <token>'"}
Cause: invoking is not the open door it used to be. INVOKE_AUTH_REQUIRED defaults to true, so POST /v1/agents/{name}/invoke and its streaming form need the same kind of credential managing a function does. The default was the other way until August 2026, and the reason it changed was measured from the public internet: an anonymous invoke returned a complete model turn for a workload the caller had nothing to do with, because name resolution is not project-scoped. The message names the setting so that an install configured differently is still diagnosable from the error alone.
Fix: send Authorization: Bearer <token> on invokes too; platformctl login covers the CLI. If you want one function to answer with no credential, that is a per-endpoint decision rather than a platform-wide switch: publish it through the gateway with auth mode none and a rate limit, set on the function itself as CAI_EXPOSE_EXTERNAL=none plus CAI_EXPOSE_RATE_LIMIT=100/minute. The rate limit is required — a public invoke path with no caller budget is an unbounded spend of the project's model key, so publishing without one is refused. See HTTP and events.
not found on a project that definitely exists
Symptom: requests against a project return 404 not found even though the project exists.
Cause: when you have no access to a project, the platform answers 404 and never 403. That is deliberate: a 403 would confirm the project exists, which lets an outsider test names. So a 404 here usually means "you are not a member", not "wrong URL".
Fix: confirm which projects you can see with platformctl projects list, and ask a project admin to add you.
Still stuck?
- Quote the
request_id. Every error carries one in its envelope; it identifies your exact request in the platform's records. - Known issues — feature details that are documented rather than fixed.
- Troubleshooting (all services) — errors that are not specific to this service.
- Get help — who to report it to, and what to include.
Two known-good references worth comparing against: the function quickstart and the runtimes page, which lists the exact handler file names and symbols per language.