karatelabs/karate · error · RuntimeException

embed(): each 'parts' entry must be an object

Error message

embed(): each 'parts' entry must be an object

What it means

When karate.embed() receives a multipart embed object ({parts: [...]}), every entry of the parts list must itself be an object describing a part (role, data/path/url, optional mime). Karate throws this when a parts entry is a string, number, array, or null instead of a map.

Solutions

  1. Wrap each entry as an object: karate.embed({parts: [{role: 'screenshot', path: 'shot.png'}]}).
  2. Validate each part object has a role plus one of data/path/url before calling embed.
  3. If embedding a single item, pass the bytes directly instead of a parts list.

Example fix

// before
karate.embed({ parts: ['shot.png'] });

// after
karate.embed({ parts: [{ role: 'image', path: 'shot.png' }] });
Defensive patterns

Strategy: validation

Validate before calling

if (parts.some(function(p){ return p == null || typeof p !== 'object' || Array.isArray(p); })) { throw new Error('each part must be an object'); }

Type guard

function isEmbedPart(p) { return p != null && typeof p === 'object' && !Array.isArray(p); }

Try / catch

try { karate.embed({ parts: parts }); } catch (e) { if (String(e.message).includes("each 'parts' entry must be an object")) { karate.log('invalid parts entry'); } throw e; }

Prevention

When it happens

Trigger: karate.embed({ name: ..., parts: ['screenshot.png'] }) or parts entries that are plain strings / null / nested lists instead of objects with role and content fields.

Common situations: Assuming parts can be plain file paths; building the parts list programmatically and pushing raw paths; JSON structures where entries ended up as strings after templating.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/e915146f71c85bbb. Report an issue: GitHub.

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/core/KarateJsBase.java:526

                return null;
            }
            // single-part (legacy)
            String mimeType = args.length > 1 && args[1] != null
                    ? args[1].toString() : KarateJsUtils.detectMimeType(first);
            String name = args.length > 2 ? args[2].toString() : null;
            byte[] data = KarateJsUtils.convertToBytes(first);
            LogContext.get().embed(data, mimeType, name);
            return null;
        };
    }

    private StepResult.Embed toMultiPartEmbed(Map<?, ?> map) {
        String name = map.get("name") != null ? map.get("name").toString() : null;
        List<StepResult.Part> parts = new ArrayList<>();
        for (Object partObj : (List<?>) map.get("parts")) {
            Object pu = unwrapJs(partObj);
            if (!(pu instanceof Map<?, ?> pm)) {
                throw new RuntimeException("embed(): each 'parts' entry must be an object");
            }
            Object roleObj = pm.get("role");
            if (roleObj == null) {
                throw new RuntimeException("embed(): each part needs a 'role'");
            }
            String role = roleObj.toString();
            String mime = pm.get("mime") != null ? pm.get("mime").toString() : null;
            Object url = pm.get("url");
            if (url != null) {
                parts.add(new StepResult.Part(role, mime, url.toString()));
                continue;
            }
            byte[] bytes;
            Object dataObj = pm.get("data");
            Object pathObj = pm.get("path");
            if (dataObj != null) {
                bytes = KarateJsUtils.convertToBytes(unwrapJs(dataObj));
            } else if (pathObj != null) {

View on GitHub (pinned to a22eb90246)