Skip to content

scaffolder-backend-module-hcl incompatible with Zod v4 — hcl:merge:files:write fails with "instance is not of a type(s) string" #87

Description

@jlowe-kb4

Summary

The hcl:merge:files:write (and related) actions fail at runtime when the consuming Backstage app forces Zod v4 via a Yarn resolution. The action handler is never reached — the error fires during Backstage's own input validation step.

Error

InputError: Invalid input passed to action hcl:merge:files:write, instance is not of a type(s) string
    at NunjucksWorkflowRunner.executeStep (NunjucksWorkflowRunner.cjs.js:277:19)

Root cause

This package declares "zod": "^3.23.8" but has no local node_modules/zod, so it resolves to whatever the consuming app provides. When that is Zod v4 (released April 2025), the schema conversion breaks silently:

  1. Backstage's parseSchemas() in @backstage/plugin-scaffolder-node only recognises two Zod v3 schema patterns (callback functions or key-value objects). A Zod v4 ZodObject matches neither, so parseSchemas() returns undefined for the input schema.
  2. However, createTemplateAction still stores the raw Zod v4 object on action.schema.input.
  3. At runtime, NunjucksWorkflowRunner sees action.schema.input is truthy and passes it to jsonschema.validate().
  4. zod-to-json-schema doesn't recognise the v4 ZodObject shape and falls back to { "type": "string" }.
  5. The actual input (an object with aSourcePath, bSourcePath, etc.) fails validation against { "type": "string" }.

Confirmed by running:

const zod = require('zod'); // v4.3.6
const zodToJsonSchema = require('zod-to-json-schema');
const schema = zod.z.object({ aSourcePath: zod.z.string(), outputPath: zod.z.string() });
console.log(zodToJsonSchema(schema)); // → { "type": "string" }

Versions

  • @seatgeek/backstage-plugin-scaffolder-backend-module-hcl: 2.0.1
  • zod (forced via Yarn resolution): 4.3.6
  • @backstage/plugin-scaffolder-node: ^0.4.10

Workaround

Pin a scoped Yarn resolution to force this package back to Zod v3:

"resolutions": {
  "@seatgeek/backstage-plugin-scaffolder-backend-module-hcl/zod": "^3.23.8",
  "zod": "^4.0.0"
}

Fix

Update the package to use Zod v4 (or declare a peer dependency on zod so consumers are warned of the incompatibility). The schema definitions themselves are straightforward and should be compatible with minor changes.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions