Limitations¶
What the plugin cannot do, or does differently from the platform, and why. Each entry names the recipe that works around it where one exists.
Reading results¶
- Logs come from Python script, wheel and JAR tasks only, and hold the last 5 MB. Notebook tasks have no logs; what a notebook printed is in
task.notebook.stdout, read from the notebook thatjobs.export_runrenders, by default only for notebook tasks that ran and did not succeed, and only for the last attempt of a retried task. That export format is not documented, so it is parsed best effort: when it cannot be read,cellsis empty,htmlkeeps the export and aUserWarningis emitted. Printed Python output was checked on serverless compute and on classic job clusters running 17.3 LTS and 13.3 LTS; Scala, R and%shcells were not. Databricks documents that on serverless compute the logs of one task can include output from other tasks in the same run; in the plugin's tests each task's logs held only its own output, but this is not guaranteed. - Notebook output keeps only the first 5 MB. A truncated output or log emits a
UserWarningnaming the task, andoutput_jsonrefuses truncated output with aValueError. - Cancelled tasks have no output. A task that ended
CANCELED, such as the tasks of a run stopped bystop_after, counts as not run: the plugin reads nooutput,logs,erroror notebook for it. Assert on the data the job wrote instead. - Resolved task parameters cost one request per task. The Jobs API fills a task's
resolved_valuesonly when the task run is fetched by its own id; on the job run the field is always null. A task run that cannot be read leavesTaskRun.parametersasNonewith a warning. - SQL rows come from SQL file tasks only.
TaskRun.sql_resultis read fromsql_output.file_output, a field ofruns/get-outputthat the Jobs API reference does not document and databricks-sdk does not model: one extra request per SQL file task that ran. A saved query task reports onlyquery_text,warehouse_idand a link to the result page (checked October 2026), so it givesNoneunlesssaved_queries="inline"runs its text as a SQL file task, which runs as the job's identity rather than the query's owner and without the SQL editor's automaticLIMIT 1000. Alert and dashboard tasks giveNone. Values are strings, a NULL isNone, and the platform keeps at most 1,000 rows for 7 days. - Task values need probes. Task values are not available through the Jobs API, so they are read by probe tasks, each a serverless task start of 7 to 30 seconds, even when its task did not run. For the same reason
upstream_fromandreplaycan only replay a run that captured its values withcapture_task_values, never a production run. - dbt artefacts are a link that expires.
TaskRun.dbt_output.artifacts_linkis a pre-signed URL that the platform signs for 30 minutes. WithLakeflow(dbt_artifacts=True)the plugin downloads it while building the run, soTaskRun.dbtis only filled for runs thatrun()orwait()built, and a tarball over 50 MB is not downloaded.report.jsonkeeps the link and leaves out the headers. - The pipeline update is found by time. The Jobs API does not say which update a pipeline task started, so
TaskRun.pipeline_updateis the update created between the task's start and end, one started by a job task first. Two pipeline tasks that update the same pipeline in one run, or anyone else updating the deployed pipeline while the task runs, can make the match ambiguous. - Alert tasks succeed whether or not the alert triggered. Assert on
TaskRun.alert_state. Only an alert that cannot be evaluated fails the task, withalert_stateERROR, and the Jobs API reports no error for it, soTaskRun.errorholds the termination message.
What the copy changes¶
- Job code must read catalog and schema from job parameters. Bundle variables are fixed at deploy time, and the plugin deploys each target once per session for each set of
variables. - Run Job tasks run the deployed child job. The copy keeps
run_job_task.job_idas deployed, so a test of a parent job starts a real run of the child, with the child'srun_asand notifications, which is not cancelled or deleted with the copy. The plugin warns, or refuses withchildren="refuse". Copying or stubbing the child is not supported. - Alert tasks and Power BI tasks run as the template has them. An alert task notifies its subscribers and has no pause flag; a Power BI task updates the semantic model whether or not
refresh_after_updateis set. Disable them withdisable=. - Pipeline tasks run the deployed pipeline unless you copy it. By default every test that runs a pipeline task updates the same deployed pipeline and its tables.
pipelines="copy"andlakeflow.pipeline()run copies instead. - A pipeline copy only moves the tables its code names without a schema. A fully qualified name such as
prod.sales.ordersstill writes where it says. The copy runs the code the lastbundle deployuploaded, as you, in development mode, without the deployed pipeline's event log. A pipeline that publishes to the Hive metastore cannot be copied, andpipelines/getreturns noparameters, so a copy carries none; set what the code reads withconfiguration=. On databricks-sdk 0.100 to 0.109,pipelines.deletetakes nocascade, so a deleted copy keeps its tables until its schema is dropped. - Stubbed outputs are literals. A stub cannot run a query or evaluate an alert, so a
Stub'soutputsreplace{{tasks.<key>.output.<path>}}references with fixed strings, in the platform's format:rowsis a JSON array of row objects andfirst_rowone such object, every value a string andNULLas an empty string. Code that reads the task's output withdbutilsrather than a reference is not affected. - Setup and teardown functions ship as source. They run as notebooks on the job's compute, so they must be self-contained. Lambdas, closures, decorated functions and functions defined in a REPL are refused. On a helper cluster older than Databricks Runtime 13.3 LTS, which has no
dbutils.widgets.getAll(),paramsholds only the declared parameters and the keys ofjob(params=...). Each costs one task start per run. - Single-task jobs keep only the target task. On a shared job cluster a task can import libraries that other tasks declared, and on its own it loses them. Declare every library on the task that needs it.
- Test-only dependencies skip serverless notebook tasks without an environment, which cannot take dependencies from the job settings; the plugin warns. A local wheel with extras cannot be installed as a cluster library, so it raises
LakeflowErrorwhen the copy has a Python task on a cluster. - Serverless auto-retry is off by default in every copy, so a test sees one attempt. Pass
disable_auto_optimization=Falseto keep the platform's behaviour, or setmax_retrieswithtask_overridesto test retries. - Forced conditions apply to the whole copy. Every run of a copy made with
force_conditionstakes the same branch; make another copy for the other branch. Condition tasks nested in a for-each task cannot be forced, and forcing a condition task thatdisabledisables, directly or downstream, raisesLakeflowError. - For-each tasks have limits of their own. An iteration cannot set task values, so
capture_task_valuesrefuses a for-each task and its nested task, and its input is reported only where the nested task passes{{input}}. Iterations are not exported tonotebook. The nested task cannot be the target ofrun(only=...).for_each_inputsworks on top-level for-each tasks, as a literal list of at most 5,000 characters as JSON, of all primitives or all objects. A lifted body sees its input as__pytest_inputjob parameters, written as an iteration would write them, and may set task values, which an iteration cannot. - Only trigger references in the settings can be simulated.
trigger=rewrites{{job.trigger.<path>}}references, so code that reads the trigger context another way still sees a run-now run, andrun.trigger_typestaysONE_TIME. A list value such astable_update.updated_tablesis passed as the string you give, because the platform's format for it has not been recorded on a triggered run. A job whose settings usetable_updateorfile_arrivalreferences cannot be copied withouttriggersetting them. dbt_commandsneeds a job without job parameters. The Jobs API refuses the legacy run parameter on a job that has job parameters, whichtest_marker,trigger=andinput=declare. Usetask_paramsinstead.single_nodeneeds dedicated access mode. It sets the explicit single node fields (num_workers,spark_confand theResourceClasstag), notkindandis_single_node. The Clusters API accepted them on aSINGLE_USERjob cluster without a policy; a cluster policy that forbidsspark.databricks.cluster.profilewould refuse them. Standard (shared) access mode clusters were not tested.- Debug notebooks replay parameters only. They set the task's resolved parameters as widgets and
%runthe task's notebook; they do not restore task values or the Spark session. A text widget holds at most 2048 bytes. Notebook tasks inside a for-each task are not covered. - Names starting with
__pytest_are reserved for the plugin's tasks, environments, widgets and parameters. Tests cannot pass them asparamsor intask_params.
Deployments and sessions¶
- The Databricks CLI is required. Deploying is only possible through the CLI, so the plugin shells out to it. There is no pure-SDK mode.
- Concurrent sessions sharing one target replace each other's wheels. A deploy from one session replaces the wheels that another session's jobs are still running.
deploy_flags=["--fail-on-active-runs"]does not prevent it, because it only sees runs of the deployed jobs. Use separate deployments per branch and identity. - Different setups of one target can collide under xdist.
Lakeflowinstances for the same target that differ invariables,sync,deploy_flagsorhelpers_pathare set up separately, so two workers can deploy the target at the same time and one can fail on the CLI's deployment lock. Give each set of variables its own target. - Serverless may run a cached wheel with the same file name. A wheel artifact without
dynamic_version: trueis uploaded under the same file name on every deploy while its version is unchanged, and databricks/cli#1621 and databricks/cli#2958 report serverless running the cached old build. A probe in the maintainers' test workspace in October 2026 did not reproduce it. The plugin warns once per session, or raises withstrict_artifacts=True. Setdynamic_version: trueon the artifact. - With
sync="none", a stale wheel is caught by its modification time only. The plugin warns when a local library file is newer than its uploaded copy by more than 60 seconds, which allows for clock skew. It misses a wheel deployed from another checkout, an older build copied over a newer one, and code changed but not rebuilt. It covers the local libraries of the bundle's jobs, not local paths inextra_dependencies. syncandnonereadbundle validateoutput, which leaves library paths local and${resources...}references unresolved. Jobs affected by that are refused, with an error that says to usedeploy.- One run at a time per copy. Each copy is created with
max_concurrent_runs: 1. Parallelism comes from separate tests, each with its own job, or from several copies in one test started withstart(). Lakeflowworks only inside pytest. It raisesLakeflowErroroutside a pytest session and outside a test, because jobs are tied to the running test for naming, tagging and teardown.Lakeflow.sweep()is the exception: it runs from any script.