Crosstalk-Solutions/project-nomad · error

guard.blocked.join(' ')

Error message

guard.blocked.join(' ')

What it means

A 422 from createCustomApp when evaluateCustomApp hard-blocks the request. The guard rejects dangerous bind mounts (e.g. mounting / or /var/run/docker.sock) and malformed image references; these blocks cannot be overridden with force=true.

Source

Thrown at admin/app/controllers/system_controller.ts:400

            })
        }

        // Reject duplicate host ports within the request — Docker would otherwise fail at
        // start time with an opaque "port is already allocated" error.
        const hostPorts = (payload.ports ?? []).map((p) => p.host)
        const duplicateHostPorts = [...new Set(hostPorts.filter((p, i) => hostPorts.indexOf(p) !== i))]
        if (duplicateHostPorts.length) {
            return response.status(422).send({
                success: false,
                message: `Duplicate host port(s): ${duplicateHostPorts.join(', ')}. Each host port can map to only one container.`,
            })
        }

        // Security guardrails: hard-block dangerous bind mounts / malformed images regardless of
        // force; surface overridable warnings (risky paths, untrusted/moving-tag images) unless forced.
        const guard = evaluateCustomApp({ image: payload.image, volumes: payload.volumes })
        if (guard.blocked.length) {
            return response.status(422).send({
                success: false,
                message: guard.blocked.join(' '),
                blocked: guard.blocked,
            })
        }
        if (!payload.force && guard.warnings.length) {
            return response.status(409).send({
                success: false,
                message: guard.warnings.join(' '),
                warnings: guard.warnings,
            })
        }

        // Advisory preflight: surface port conflicts before creating the record so a failed
        // install doesn't leave a phantom card. The user can re-submit with force=true to override.
        if (!payload.force && hostPorts.length) {
            const { conflicts } = await this.dockerService.checkPortConflicts(hostPorts)
            if (conflicts.length) {

View on GitHub (pinned to 0bd1c6f4f9)

Solutions

  1. Read guard.blocked in the response — each entry names the rejected volume/image and why
  2. Remove or narrow the dangerous bind mount (mount only a specific subdirectory)
  3. Fix the image reference to a valid registry/name:tag form
  4. Note: force=true will NOT bypass blocked items by design

Example fix

// before
volumes: [{ host: '/var/run/docker.sock', container: '/var/run/docker.sock' }]
// after
volumes: [{ host: '/opt/myapp/data', container: '/data' }]
Defensive patterns

Strategy: validation

Validate before calling

const blocked = evaluateCustomApp({ image, volumes }); // if available client-side
const dangerous = volumes?.some((v) => /^\/(var\/run\/docker\.sock|etc|var)?$/.test(v.host));
if (dangerous) disableSubmit();

Type guard

const isSafeMount = (h: string) => !/^\/(var\/run\/docker\.sock|root|etc|var|bin|sbin|usr|lib)?$/g.test(h);

Prevention

When it happens

Trigger: POST create-custom-app with a volume like /:/host or /var/run/docker.sock:/var/run/docker.sock, or an image string that fails reference validation.

Common situations: User tries to give an app Docker control via the socket mount, mounts the host root, or typos the image name so it doesn't parse as a valid reference.

Related errors


AI-assisted analysis of Crosstalk-Solutions/project-nomad@0bd1c6f4f9 (2026-08-27). Data as JSON: /api/errors/969337cbab9702f8. Report an issue: GitHub.