name: 03-appkit-deploy
description: >
Deploy a Databricks AppKit application to Databricks Apps. Covers config
validation, build verification, deployment, UI verification, error diagnosis
with fix loop, and workspace app limit handling. Use when asked to deploy an
AppKit app, push to production, ship the app, or troubleshoot a failed deploy.
Triggers on "deploy app", "push to databricks", "ship app", "deploy appkit",
"databricks apps deploy", "fix deploy error", "app won't start".
license: Apache-2.0
compatibility: Requires a built AppKit project with Node.js v22+ and Databricks CLI >= 0.295.0
allowed-tools: Bash(databricks:) Bash(npm:) Bash(curl:) Bash(node:) Read
clients: [ide_cli, genie_code]
bundle_resource: apps
deploy_verb: apps_deploy
deploy_note: >
IDE: databricks apps deploy --profile $PROFILE (local Node build + bundle sync + start).
Genie Code: run databricks apps deploy via runDatabricksCli where the project page
allows it (omit --profile — pre-authenticated), else fall back to the SDK
w.apps.deploy(<name>, AppDeployment(source_code_path=…, mode=AppDeploymentMode.SNAPSHOT))
via executeCode. The frontend build runs server-side in the container, so no local
npm install / npm run build is required on Genie Code (Gap 4 resolved).
coverage: full
metadata:
author: prashanth subrahmanyam
version: "1.2.0"
domain: apps
role: deploy
standalone: true
last_verified: "2026-06-02"
volatility: medium
upstream_sources: [] # Deployment workflow / fix-loop is project-specific; canonical upstream in See Also.
Deploy Databricks AppKit Applications
Deploy an AppKit project to Databricks Apps, verify it runs, and fix common errors.
When to Use
- Deploying an AppKit app to Databricks Apps (first deploy or redeployment)
- Verifying a deployed app loads correctly
- Diagnosing and fixing deploy failures
- Freeing workspace app slots when the limit is reached
Not for scaffolding. Use 01-appkit-scaffold to create a new project.
Not for building features. Use 02-appkit-build to implement UI and backend code.
Prerequisites
Before deploying, ensure:
- The app builds locally (
npm run buildsucceeds) $APP_NAMEand$PROFILEare set by the calling prompt. If a.vibecoding-state.mdexists from a prior phase, use theAPP_NAME,PROFILE, and workspace URL values from it directly — do not re-derive them withdatabricks current-user meordatabricks auth profiles.- The app directory contains
app.yamlanddatabricks.yml - If deploying to a different workspace than where the app was scaffolded: update the
hostindatabricks.yml, updatesql_warehouse_idfor the new workspace, and remove stale bundle state withrm -rf $APP_NAME/.databricks - If no CLI profile exists for the target workspace, create one per PRE-REQUISITES §11 before deploying (the IDE/CLI auth path; it uses
auth login, not the interactiveconfigureflow that fails in automated/agent contexts). IDE/CLI only — on Genie Code the runtime is already authenticated to its host workspace; skip profile creation and drop--profilefrom every command (see the routing note below). - All commands in this skill assume the working directory is
apps_lakebase/. Paths like$APP_NAME/app.yamlare relative toapps_lakebase/, not the repo root. On Genie Code, this is<artifact_root>/apps_lakebase/inside your git-cloned workshop project (NOT the read-only.assistant/skills/copy) —cdthere first; never operate from/tmp. - Do NOT run
rm -f package-lock.json && npm installlocally before deploying. The platform'snpm installdepends on lockfile stability; regenerating the lockfile locally causesENOTEMPTY/Exit handler never calledfailures during platform install. See references/lockfile-and-recreation.md for the full rule, scenario table, and recovery ladder — plus the Lakebase ownership consequences of app recreation.
Working in Genie Code (deploy routing)
This skill is written for the IDE/CLI path — the commands below are correct as-is when you have a local terminal, Node.js, and a CLI profile. If you are running in Genie Code (no local toolchain, pre-authenticated to one workspace), apply these four substitutions to every step and you do not need to re-read this note per command:
| IDE/CLI (as written) | Genie Code substitution |
|---|---|
databricks <cmd> … --profile $PROFILE |
run databricks <cmd> … via runDatabricksCli, omit --profile (ambient auth) |
databricks apps deploy … (when the project page blocks it) |
SDK fallback via executeCode: w.apps.deploy(<name>, AppDeployment(source_code_path=<workspace path>, mode=AppDeploymentMode.SNAPSHOT)) |
npm run build / npm run dev (local) |
skip — the frontend build runs server-side in the container during deploy; the build error is not readable from compute (databricks apps logs <name> → OAuth error) — read it at <app-url>/logz in a browser instead |
databricks bundle deploy (targetless) |
add --target dev (a targetless bundle deploy is guardrail-blocked on Genie Code) |
Everything else (config validation, log streaming, the fix loop, error table) is identical across clients. Inline > **Client note — Genie Code:** callouts below flag the few steps where the behavior — not just the syntax — differs. See skills/genie-code-environment for the full behavioral manifest.
Before You Begin
The upstream Databricks agent-skills repo and AppKit docs are the source of truth for deploy commands, platform rules, and options.
- Agent Skills (deploy patterns, platform rules): https://github.com/databricks/databricks-agent-skills/blob/main/skills/databricks-apps/SKILL.md
- Platform guide (SP permissions, runtime constraints, errors): https://github.com/databricks/databricks-agent-skills/blob/main/skills/databricks-apps/references/platform-guide.md
- App management: https://databricks.github.io/appkit/docs/app-management
- Configuration: https://databricks.github.io/appkit/docs/configuration
- AppKit docs (in-terminal):
npx @databricks/appkit docs "app-management" - CLI help:
databricks apps deploy --help
The bundled reference at references/app-management.md covers commonly used commands as a fallback when live docs cannot be reached.
Do NOT improvise workarounds. If a deployment fails, check the app logs and match the error against the Common Errors table below. Do NOT add npm lifecycle hooks (
preinstall,postinstall), platform-detection conditionals, or workarounds that skip the platform's build pipeline. These consistently cause cascading failures that are harder to diagnose than the original error. When in doubt, consult the authoritative sources above.
Authoritative References
The databricks-agent-skills repository contains the canonical AppKit deployment patterns. When in doubt, consult these references:
| Topic | Reference |
|---|---|
| AppKit scaffolding, validation, workflow | databricks-apps SKILL.md |
| Platform rules (SP permissions, runtime limits, common errors) | platform-guide.md |
| AppKit project structure and checklists | appkit/overview.md |
| Lakebase pool, CRUD, schema ownership | appkit/lakebase.md |
| Smoke tests and Playwright guidance | testing.md |
| Lakebase two-phase deploy (Phase 1/2 binding) | plugin-lakebase.md — read before any Lakebase deploy |
| AppKit docs (in-terminal) | npx @databricks/appkit docs "app-management" |
Platform Constraints (from platform-guide.md)
These runtime constraints affect deployment and troubleshooting:
- Startup timeout: App must start within 10 minutes (including dependency installation)
- HTTP proxy timeout: 120 seconds per request (not configurable; use WebSockets for long operations)
- Max apps per workspace: 100
- Max file size: 10 MB per file in bundle
- Filesystem: Ephemeral — no persistent local storage; use UC Volumes or Lakebase
- No shell in
command:app.yamlcommanddoes not run in a shell; env vars outsideapp.yamlare inaccessible - Graceful shutdown: SIGTERM → 15 seconds → SIGKILL
- Logging: Only stdout/stderr captured; file-based logs are lost on container recycle
- Destructive updates:
bundle run/apps updatedoes full replacement and can wipe OBO scopes
Platform Build Pipeline
When databricks apps deploy pushes code to the platform, the following sequence runs inside the container:
- Download source — workspace files are extracted into
/home/app/ npm install— installs dependencies frompackage.json/package-lock.json. Runs in production mode (NODE_ENV=production), sodevDependenciesare skipped.npm run build(if thebuildscript exists) — compiles the project.prebuildhooks fire automatically before this step.- Run
command— executes thecommandfromapp.yaml(e.g.,npm run start)
Hard timeout: The entire sequence (steps 1-4) must complete within 10 minutes. If npm install or build exceeds this, the deploy fails with App process did not start within 10 minutes.
Critical rules:
- NEVER add
preinstallorpostinstallscripts that modifynode_modules. These create infinite loops or corrupt the install. - NEVER add platform-detection conditionals (e.g.,
[ "$HOME" = '/home/app' ]) to skip build steps. - NEVER modify the scaffold's
package.jsondependency versions, aliases, or overrides. Ifrolldown-vite,@playwright/test, or other packages were included bydatabricks apps init, leave them as-is — the scaffold is tested to deploy on the platform. - DO let scaffolded
prebuildhooks run (appkit sync,appkit generate-types). Warnings about@ast-grep/napiare harmless and guarded by2>/dev/null; true. - DO note that
databricks bundle deploy(anddatabricks apps deploywhich calls it internally) uses.gitignorepatterns for file exclusion — NOT.databricksignore.
Authoritative source: Databricks Apps deploy — deployment logic and post-deployment behavior.
Client note — Genie Code (verified): because this build pipeline runs server-side in the container, a Genie Code user with no local npm can deploy by editing source directly in the workspace and triggering a deployment via the SDK —
w.apps.deploy(<name>, AppDeployment(source_code_path=<workspace path>, mode=AppDeploymentMode.SNAPSHOT)). This was tested end-to-end: editing an un-builtclient/src/App.tsxand redeploying produced deploy statusBuilding app…and the edited string appeared in the server-built JS bundle (/assets/index-*.js). No localnpm install/npm run buildand no pre-synceddist/are required on Genie Code.
Package Lock Management
The platform's npm install depends on package-lock.json stability. Scenario table:
| Lockfile state | Platform behavior | Action |
|---|---|---|
| Present, matches platform cache | Fast install | Deploy |
| Regenerated locally with foreign registry URLs | Mixed URLs → ENOTEMPTY / Exit handler never called after ~3 min |
Revert or delete lockfile, redeploy |
| Absent | Fresh resolve, slower but succeeds | Acceptable for first deploy |
Refreshed via npm install --package-lock-only |
Keeps lockfile coherent, no tarball install | Preferred when bumping dep versions |
NEVER run
rm -f package-lock.json && npm installlocally before deploying. The regenerated lockfile picks up your local npm proxy URLs, breaking platform install. If you must refresh, use--package-lock-only, or delete the lockfile and let the platform resolve.
Client note — Genie Code: the "Absent → acceptable for first deploy" row does NOT apply on the SDK SNAPSHOT path. With no
package-lock.json, the deploy hard-fails at the source-export phase in ~10s (RESOURCE_DOES_NOT_EXIST), beforenpm installruns — so the lockfile is a hard requirement, not optional. Never delete it as a "reset"; change deps by editingpackage.jsonand keeping the lockfile consistent.
Full recovery ladder + prevention rules: references/lockfile-and-recreation.md.
App Deletion Is Destructive (Lakebase Ownership)
Deleting and recreating a Databricks App assigns a new Service Principal UUID. Lakebase schemas owned by the old SP become inaccessible (permission denied for schema, must be owner of table) even though psql as admin still sees them.
Fix, as workspace admin via databricks psql:
DROP SCHEMA IF EXISTS <schema_name> CASCADE;
-- Then redeploy — the new SP's DDL will recreate and own the schema.
Prevention: avoid app deletion. If deploys are broken, use the lockfile recovery ladder first. App deletion is a last resort.
Step 1: Validate Configuration
Verify app.yaml has a startup command:
grep -E "build/index\.mjs|npm.*start" $APP_NAME/app.yaml
Accepted patterns:
command: ['npm', 'run', 'start']— AppKit default (scaffold output)command: [node, build/index.mjs]— legacy / alternative
If using npm run start, verify the start script in package.json points to the correct built output (e.g., node dist/server.js).
If no startup command is present, add the default:
command:
- npm
- run
- start
Check that required environment bindings are present. At minimum, apps using the analytics plugin need:
grep "DATABRICKS_WAREHOUSE_ID" $APP_NAME/app.yaml
You should see valueFrom: sql-warehouse. If missing, add it:
env:
- name: DATABRICKS_WAREHOUSE_ID
valueFrom: sql-warehouse
The calling prompt may require additional plugin-specific env vars (e.g., LAKEBASE_ENDPOINT for Lakebase). Validate those before proceeding.
Verify databricks.yml references the correct $APP_NAME:
grep "name:" $APP_NAME/databricks.yml | head -2
Note: The AppKit scaffold does not include a name: field in app.yaml — the app name is defined only in databricks.yml under bundle.name and resources.apps.
Run the AppKit validator to check app.yaml schema, resource bindings, and manifest validity:
cd $APP_NAME && databricks apps validate --profile $PROFILE
Fix any reported errors before proceeding.
Client note — Genie Code:
databricks apps validateis hard-blocked viarunDatabricksCli(not allow-listed). Skip this gate on Genie Code. The platform runs the same validation server-side during the build pipeline, but those build logs are not readable from compute —databricks apps logs <name>returns an OAuth-token error. Read the authoritative schema/compile signal at<app-url>/logzin a browser (where you are already authenticated) instead.
Cross-validate valueFrom references against databricks.yml resources. Every valueFrom: in app.yaml must have a matching resource declaration in databricks.yml. If not, databricks apps deploy (which runs bundle deploy internally) will fail to resolve the resource and the env var will be empty at runtime.
for ref in $(grep 'valueFrom:' $APP_NAME/app.yaml | awk '{print $2}'); do
if ! grep -q "$ref" $APP_NAME/databricks.yml 2>/dev/null; then
echo "ERROR: app.yaml references valueFrom: $ref but no matching resource in databricks.yml"
echo " bundle deploy will strip manually-attached resources. Add the resource to databricks.yml."
fi
done
This catches the common failure where Lakebase postgres resources were attached via REST API or databricks apps update but not declared in databricks.yml — bundle deploy resets the resource list on every deploy, stripping anything not in the bundle config.
Check for Lakebase project conflicts with bundle declarations. If databricks.yml declares postgres_projects and the project already exists, the correct action depends on who created it:
- Bundle created it (Phase 1 deploy already ran,
.databricks/state exists): Keeppostgres_projects. Terraform state tracks it. Phase 2 redeploy is idempotent. See04-appkit-plugin-add/references/plugin-lakebase.mdfor Phase 2 binding instructions. - Created outside the bundle (CLI, UI, or different bundle; no
.databricks/state): Removepostgres_projectsand skip to Phase 2 binding. Terraform has no state for it.
CRITICAL: Never remove
postgres_projectsfromdatabricks.ymlafter a bundle deploy has created the project. Removing a Terraform-tracked resource declaration tellsbundle deployto destroy the resource. This causes a cascading failure: project destroyed → re-adding hits soft-delete retention ("already exists" for ~5-10 min) → manual recreation required outside the bundle.
PROJECT_ID=$(grep -A2 'postgres_projects:' $APP_NAME/databricks.yml | grep 'project_id:' | awk '{print $2}' | tr -d "'" | tr -d '"')
if [ -n "$PROJECT_ID" ]; then
EXISTS=$(databricks postgres list-projects --profile $PROFILE --output json 2>/dev/null \
| jq -e --arg pid "$PROJECT_ID" '[.[] | select(.name | contains($pid))] | length > 0' 2>/dev/null)
BUNDLE_STATE=$(test -d "$APP_NAME/.databricks" && echo "true" || echo "false")
if [ "$EXISTS" = "true" ] && [ "$BUNDLE_STATE" = "true" ]; then
echo "OK: Lakebase project '$PROJECT_ID' exists and is tracked by this bundle. Keep postgres_projects."
elif [ "$EXISTS" = "true" ] && [ "$BUNDLE_STATE" = "false" ]; then
echo "WARNING: Lakebase project '$PROJECT_ID' exists but is NOT tracked by this bundle."
echo " Remove postgres_projects from databricks.yml to avoid 'already exists' Terraform errors."
echo " Keep only app.resources.postgres binding."
fi
fi
Step 2: Build
Run npm run build locally to catch TypeScript and compilation errors early. The deploy pipeline rebuilds on the platform, but catching errors locally avoids a slow deploy-fail-fix cycle.
cd $APP_NAME
npm run build
This must complete without errors. A successful build produces the output referenced by app.yaml's command (typically build/index.mjs or dist/server.js).
Client note — Genie Code: this local pre-build is an IDE/CLI convenience, not a hard gate — there is no local Node toolchain on Genie Code. Skip Step 2 entirely and deploy directly (Step 3); the platform runs the same
npm install+npm run buildserver-side during the deploy pipeline. TypeScript/compile errors do NOT come back throughdatabricks apps logs <name>(it returns an OAuth error from compute); they appear only at<app-url>/logzin a browser — read the exacterror TS…line there and feed it into the Step 5 fix loop. Because there is no localtsc/npmto catch them first, run the import-specifier grep gate (see the99-deploy_databricks_app.genie-code.mdfork, Step 2b) before deploying. (Build-runs-server-side was verified end-to-end — see the Platform Build Pipeline note above.)
Verify the build output exists before deploying:
ls build/index.mjs 2>/dev/null || ls dist/server.js 2>/dev/null || echo "WARNING: No build output found — check app.yaml command"
If there are TypeScript or build errors, fix them before proceeding.
Fixing TS6133 (Unused Import/Variable) Errors
These are the most common build errors at deploy time. Before removing any symbol:
- Grep the full file for each symbol name — do not rely on reading only the import section.
- If the import has other used names, remove only the unused name from the import list.
- If ALL names from a module are unused, delete the entire import line. Never produce
import {} from "..."— this is a side-effect import that loads the module for nothing. - If the error is on a variable declaration (e.g.,
const navigate = useNavigate()), remove both the declaration and its import.
After fixes, run the linter on edited files before rebuilding.
Step 3: Deploy
Before running the deploy, verify the lockfile has not been regenerated locally — a regenerated lockfile will often fail on the platform with ENOTEMPTY after ~3 minutes:
cd $APP_NAME
test -f package-lock.json && git diff --quiet -- package-lock.json \
|| echo "WARN: package-lock.json modified locally; review references/lockfile-and-recreation.md before deploy"
If the warning fires, read references/lockfile-and-recreation.md and apply the recovery ladder (revert → delete → --package-lock-only refresh) before proceeding.
Deploy using the AppKit CLI pipeline — a single command that builds the frontend, syncs code to the workspace via bundle deploy, and starts the app:
cd $APP_NAME
databricks apps deploy --profile $PROFILE
This is equivalent to running npm run build + databricks bundle deploy + databricks apps start in sequence.
Client note — Genie Code: run
databricks apps deploy(no--profile) throughrunDatabricksClifrom the$APP_NAMEdirectory. If the project page blocksapps deploy, use the SDK fallback viaexecuteCode—w.apps.deploy(<name>, AppDeployment(source_code_path=<workspace path>, mode=AppDeploymentMode.SNAPSHOT))— which triggers the same server-side build (no pre-synceddist/needed). Then pollw.apps.get(<name>)for theBuilding app…→ACTIVEtransition; onFAILED, read the build error at<app-url>/logzin a browser (databricks apps logs <name>returns an OAuth error from compute).
For faster iteration after the first deploy, skip the build step:
databricks apps deploy --skip-build --profile $PROFILE
Resource-sensitive deploys:
databricks apps deployrunsbundle deployinternally, which resets the app's resource list to matchdatabricks.yml. If resources were added outside the bundle (e.g., via REST API), a bundle deploy removes them. In that case, sync code first withdatabricks bundle deploy --profile $PROFILE, then trigger a run separately withdatabricks apps deploy --profile $PROFILE(which rebuilds from the already-synced workspace source without resetting resources). If no code changes are needed, skip redeployment entirely.
For Lakebase Autoscaling, use postgres_project/postgres_branch/postgres_endpoint resources (CLI v0.287.0+) if you want bundle-managed project lifecycle. For Lakebase Provisioned, use database_instance + app.resources[].database (CLI v0.265.0+). Do not mix the two models.
Wait for completion — typically 1-3 minutes for redeployments, 3-5 minutes for first deploys. Do not treat longer waits as failures until 7+ minutes have elapsed.
Verify the app is running before proceeding:
databricks apps get $APP_NAME --output json --profile $PROFILE | jq '{status: .status.state, compute: .compute_status.state}'
Note:
status.statemay benullfor up to 60 seconds after deployment even when the app is healthy. The definitive health signal iscompute_status.state: "ACTIVE"plus clean logs showing the server is listening. IfcomputeisACTIVEbutstatusisnull, the app is running — proceed to Step 4.
If compute is not ACTIVE, wait 30 seconds and re-check. Use databricks apps logs $APP_NAME --follow --profile $PROFILE to stream logs in real-time instead of polling repeatedly.
Step 4: Verify UI Loads
Run the verification script — it handles polling, retries, and health checks in one call:
bash scripts/verify-deploy.sh $APP_NAME $PROFILE
Exit code 0 = healthy. Exit code 1 = still starting (wait 30s, re-run). Exit code 2 = error (proceed to Step 5).
Manual fallback (only if scripts/verify-deploy.sh does not exist):
APP_URL=$(databricks apps get $APP_NAME --output json --profile $PROFILE | jq -r '.url')
echo "App URL: $APP_URL"
Open $APP_URL in a browser. You should see the React application — not an error page or JSON.
If the page shows an error or doesn't load, proceed to Step 5.
Testing Deployed App APIs
Databricks Apps require authentication for all API requests. Generate a bearer token before testing:
TOKEN=$(databricks auth token --profile $PROFILE | jq -r '.access_token')
curl -s -H "Authorization: Bearer $TOKEN" "$APP_URL/api/health" | jq .
If curl returns HTML (a login page) or 401, the token has expired. Re-run the TOKEN=... line to refresh it. Tokens are short-lived (~1 hour).
Client note — Genie Code:
databricks auth tokenis hard-blocked viarunDatabricksCli, and a rawAuthorization: Bearerheader (even from SDKw.config.token) is rejected by AppKit's OAuth middleware (/api/health→401;/→302). Two working ways to test a deployed app from Genie Code:
- Browser (simplest manual verify): open the app URL (SDK
w.apps.get(<name>).url) — the Databricks Apps OAuth flow establishes the session automatically. Confirm the React UI actually renders (a green deploy with a client-side runtime crash still shows a blank/error page — the scaffold'sErrorBoundarysurfaces the stack). For backend/build logs, open<app-url>/logzin the same browser;databricks apps logs <name>returns an OAuth error from Genie compute.- Programmatic (
executeCode, for automated/api/*testing): replay the 3-hop Apps OAuth handshake in onerequests.Session()so the CSRF cookie persists (PKCE match), then reuse the session for all calls:The single Session must carry the CSRF cookie through all 3 hops or the callback returnsimport requests from databricks.sdk import WorkspaceClient w = WorkspaceClient(); app_url = w.apps.get("<name>").url s = requests.Session() r1 = s.get(app_url, allow_redirects=False) # __Host-databricksapps_csrf cookie r2 = s.get(r1.headers["location"], # Databricks OIDC authorize headers={"Authorization": f"Bearer {w.config.token}"}, allow_redirects=False) s.get(r2.headers["location"]) # /.auth/callback → session cookie print(s.get(f"{app_url}/api/health").status_code) # now authenticated403.
Step 5: Check Logs and Fix Errors (up to 3 iterations)
Stream the app logs:
databricks apps logs $APP_NAME --tail-lines 100 --profile $PROFILE
If errors exist:
- Identify the error from the log output
- Apply the fix in the app directory
- Redeploy:
databricks apps deploy --profile $PROFILE- For config-only changes (e.g.,
app.yamlordatabricks.yml):databricks apps deploy --skip-build --profile $PROFILE
- For config-only changes (e.g.,
- Check logs again
If no errors: deployment is successful.
Repeat up to 3 times. If errors persist after 3 attempts, report them for manual investigation.
Client note — Genie Code (fix loop): Step 1 above (
databricks apps logs) does not work — it returns an OAuth error from compute, and the SDK/RESTdeployment.status.messageonly says "check /logz". OnFAILED, route to the/logz-human loop: print<app-url>/logz, have the operator open it (already authenticated) and paste back the exacterror TS…/Vite line, fix that file:line, and redeploy. If no browser is available, use the 2–3-file batch ladder (revert to the lastSUCCEEDEDsource, re-apply changes 2–3 files at a time, redeploy after each ~50s batch; the batch that flips green→FAILEDholds the break). See the99-deploy_databricks_app.genie-code.mdfork Steps 2b/3 for the full procedure.
Before diagnosing errors: Run
npx @databricks/appkit docs "app-management"anddatabricks apps deploy --helpfor the latest CLI options and deployment behavior. These are the source of truth for deploy commands.
Common Errors
| Error | Cause | Fix |
|---|---|---|
Cannot find module 'build/index.mjs' |
Build output missing or app.yaml command wrong |
Verify app.yaml command matches the build output (see Step 1) and npm run build succeeds |
DATABRICKS_WAREHOUSE_ID is not set |
Analytics plugin can't find the warehouse | Add valueFrom: sql-warehouse for DATABRICKS_WAREHOUSE_ID in app.yaml |
| Connection refused / timeout on SQL queries | SQL warehouse starting up or stopped | Wait 30s and retry; check warehouse is running in the workspace |
| TypeScript / build errors during deploy | Compilation issues in server/ or client/ |
Run npm run build locally, fix errors, redeploy |
ERR_MODULE_NOT_FOUND for @databricks/appkit |
Dependencies not installed | Verify package.json lists @databricks/appkit and @databricks/appkit-ui; redeploy |
databricks apps restart -> command not found |
restart subcommand does not exist |
Always redeploy: databricks apps deploy --profile $PROFILE. There is no restart command. |
App loses resources after stop then start |
stop/start cycle detaches manually-attached resources (e.g., postgres) |
Avoid stop/start for apps with non-bundle resources. Redeploy instead. |
databricks api put/patch with complex JSON silently fails |
The CLI api subcommand does not reliably handle nested JSON payloads |
Use curl with bearer token for REST API calls that require complex JSON bodies |
error resolving resource postgres for env LAKEBASE_ENDPOINT: resource postgres not found |
app.yaml uses valueFrom: postgres but no postgres resource declared in databricks.yml; bundle deploy stripped manually-attached resources |
Add postgres_project/postgres_branch/postgres_endpoint resources to databricks.yml app definition; see 05-appkit-lakebase-wiring skill prerequisites |
app.yaml syntax / validation error |
Invalid YAML or bad valueFrom reference |
Run databricks apps validate --profile $PROFILE to diagnose |
| Env vars not available at startup | command does not run in a shell; env vars outside app.yaml are inaccessible |
Define all needed variables in app.yaml's env section |
PERMISSION_DENIED after deploy |
SP missing permissions on declared resources | Ensure resources in databricks.yml have permission field; platform auto-grants on deploy |
File is larger than 10485760 bytes |
Bundled file exceeds 10 MB limit | Use requirements.txt/package.json for deps; do not bundle large artifacts |
| 504 Gateway Timeout | Request exceeded 120s proxy timeout | Use WebSockets for long operations; SSE may be buffered |
| OBO scopes missing after deploy | apps update / bundle run does full replacement, can wipe scopes |
Re-apply OBO scopes after each deploy that modifies resources |
project with such id already exists in the workspace |
Lakebase project was recently deleted; platform has ~5-10 min soft-delete retention | Wait 5-10 min for retention to expire, then retry. Or create the project manually via databricks postgres create-project with a different project ID. If using bundle, clear .databricks/ state before retry. |
Failed to create role for SP... NOT_FOUND: project id not found |
postgres_projects was removed from databricks.yml, causing Terraform to destroy the project |
Re-add postgres_projects to databricks.yml. If the project ID is stuck in soft-delete, wait 5-10 min or create manually via CLI. See the Lakebase project conflict check in Step 1. |
ENOTEMPTY: directory not empty, rmdir '.../node_modules/<pkg>' or Exit handler never called during platform Installing packages... |
package-lock.json regenerated locally (e.g., after switching npm registries or running rm -f package-lock.json && npm install), producing mixed registry URLs the platform cache cannot satisfy |
Revert the lockfile (git checkout -- package-lock.json); if that isn't possible, delete the lockfile and redeploy for a fresh platform resolve. Full recovery ladder + prevention rule in references/lockfile-and-recreation.md. |
permission denied for schema <name> (or relation does not exist) after deleting and recreating the app |
Recreated app got a new SP UUID; Lakebase schemas are still owned by the old SP | DROP + recreate affected schemas as a workspace admin so the new SP owns them. Detailed steps in references/lockfile-and-recreation.md (Rule 2). |
unknown field: endpoint_name / missing required field: name from databricks bundle validate |
serving_endpoint app resource in databricks.yml uses endpoint_name — the schema field is name |
Rename to name: in the serving_endpoint block. See 04-appkit-plugin-add/references/plugin-serving.md ("Add Serving Endpoint as App Resource") for the correct snippet. |
The calling prompt may define additional plugin-specific errors (e.g., Lakebase connection or permission errors). Check those if the errors above don't match.
If the Workspace App Limit Is Reached
If deployment fails because the workspace has hit its app limit, do NOT rename your app. Instead, free up a slot by removing the oldest stopped app:
Find the oldest stopped app:
OLDEST=$(databricks apps list -o json --profile $PROFILE | jq -r '[.[] | select(.compute_status.state == "STOPPED")] | sort_by(.update_time) | .[0] | .name // empty')
if [ -z "$OLDEST" ]; then
echo "No stopped apps to delete. Manual workspace cleanup needed."
else
echo "Deleting oldest stopped app: $OLDEST"
databricks apps delete "$OLDEST" --profile $PROFILE
sleep 10
fi
Retry the deployment.
If the limit error persists, repeat with the next oldest stopped app — but stop after 3 total attempts (increase the wait to 20s, then 40s between retries). If it still fails after 3 tries, stop and report the issue for manual workspace cleanup. Never delete apps in RUNNING state.
Quick Reference
| Task | Command |
|---|---|
| Build | npm run build |
| Validate config | databricks apps validate --profile $PROFILE |
| Deploy (full) | databricks apps deploy --profile $PROFILE |
| Deploy (skip build) | databricks apps deploy --skip-build --profile $PROFILE |
| Sync code only (no restart) | databricks bundle deploy --profile $PROFILE |
| Get app URL | databricks apps get $APP_NAME --output json --profile $PROFILE | jq -r '.url' |
| Stream logs | databricks apps logs $APP_NAME --tail-lines 100 --profile $PROFILE |
| Follow logs live | databricks apps logs $APP_NAME --follow --profile $PROFILE |
| Search logs | databricks apps logs $APP_NAME --follow --search ERROR --profile $PROFILE |
| Stop app | databricks apps stop $APP_NAME --profile $PROFILE |
| Start app | databricks apps start $APP_NAME --profile $PROFILE |
| Delete app | databricks apps delete $APP_NAME --profile $PROFILE |
| List all apps | databricks apps list --profile $PROFILE |
| AppKit deploy help | databricks apps deploy --help |
| Live docs | npx @databricks/appkit docs "app-management" |
See Also
- Authoritative upstream: databricks-agent-skills /
databricks-apps— canonical deploy guidance for Databricks Apps. - AppKit references:
appkit, App management, Configuration, Project setup, LLM guide. - Databricks Apps docs: Deploy, App runtime.