Skip to main content
Slayemus
Participant
Jul 19, 2025 4:06 PM UTC
Answered

After Effects Scripting: "Object is Invalid" Error with Stroke and Fill Properties

function convertTextToShapes() {
    var myComp = app.project.activeItem;
    if (!myComp || !(myComp instanceof CompItem)) {
        alert("please select a composition.");
        return null;
    }
    var selectedLayers = myComp.selectedLayers;
    var textLayer = selectedLayers[0];
    if (!(textLayer instanceof TextLayer)) {
        alert("please select a text layer.");
        return null;
    }
    app.beginUndoGroup("convert text to shapes");

    try {
        var originalTextLayerName = textLayer.name;
        app.executeCommand(3781);

        var shapeLayer = myComp.selectedLayers[0];
        shapeLayer.name = originalTextLayerName + "-shapes";

        // textLayer.enabled = false;
        // textLayer.remove();
        return shapeLayer;
    } catch (e) {
        alert("something went wrong." + e.toString());
        return null;
    } finally {
        app.endUndoGroup();
    }
};

var convertedShapeLayer = convertTextToShapes();
app.beginUndoGroup("add expressions to shapes");

(function addExpressions(shapeLayer) {
    var contents = shapeLayer.property("Contents");
    for (var i = 1; i <= contents.numProperties; i++) {
        var group = contents.property(i);
        if(group.matchName == "ADBE Vector Group") {
            var groupContents = group.property("Contents");
            var charIndex = i - 1;

            groupContents.property(2).remove();
            groupContents.property(2).remove();

            var strokes = groupContents.addProperty("ADBE Vector Graphic - Stroke");
            strokes.name = "setStrokes";
            var fills = groupContents.addProperty("ADBE Vector Graphic - Fill");
            fills.name = "setFills";
            alert(strokes.name + " " + fills.name);

            var trimPaths = groupContents.addProperty("ADBE Vector Filter - Trim");
            trimPaths.name = "setTrimPaths";

            strokes.property("ADBE Vector Stroke Width").setValue(1);
            strokes.property("ADBE Vector Stroke Color").setValue([1, 1, 1, 1]);

            fills.property("Opacity").expression = 
                            'function getBezierX(t, p1x, p2x) {\r' +
                '    return 3 * Math.pow(1 - t, 2) * t * p1x + 3 * (1 - t) * Math.pow(t, 2) * p2x + Math.pow(t, 3);\r' +
                '}\r' +
                '\r' +
                'function getBezierY(t, p1y, p2y) {\r' +
                '    return 3 * Math.pow(1 - t, 2) * t * p1y + 3 * (1 - t) * Math.pow(t, 2) * p2y + Math.pow(t, 3);\r' +
                '}\r' +
                'function cubicBezierInternal(inputTime, x1, y1, x2, y2) {\r' +
                '    var t = Math.max(0, Math.min(1, inputTime));\r' +
                '\r' +
                '    if (t === 0) return 0;\r' +
                '    if (t === 1) return 1;\r' +
                '\r' +
                '    var start = 0.0;\r' +
                '    var end = 1.0;\r' +
                '    var u = t;\r' +
                '    var iterations = 8;\r' +
                '    for (var i = 0; i < iterations; i++) {\r' +
                '        var guessX = getBezierX(u, x1, x2);\r' +
                '        if (guessX < t) {\r' +
                '            start = u;\r' +
                '        } else {\r' +
                '            end = u;\r' +
                '        }\r' +
                '        u = (start + end) / 2;\r' +
                '    }\r' +
                '    return getBezierY(u, y1, y2);\r' +
                '}\r' +
                'function animateWithBezier(animStartTime, animDuration, startValue, endValue, bezierParams) {\r' +
                '    var x1 = bezierParams[0];\r' +
                '    var y1 = bezierParams[1];\r' +
                '    var x2 = bezierParams[2];\r' +
                '    var y2 = bezierParams[3];\r' +
                '\r' +
                '    var normalizedTime = (time - animStartTime) / animDuration;\r' +
                '\r' +
                '    var easedProgress = 0;\r' +
                '    if (time >= animStartTime && time <= animStartTime + animDuration) {\r' +
                '        easedProgress = cubicBezierInternal(normalizedTime, x1, y1, x2, y2);\r' +
                '    } else if (time > animStartTime + animDuration) {\r' +
                '        easedProgress = 1;\r' +
                '    }\r' +
                '    return startValue + (endValue - startValue) * easedProgress;\r' +
                '}\r' +
                '\r' +
                'var BEZIER_EASE = [0.25, 0.1, 0.25, 1.0];\r' +
                'var BEZIER_EASE_IN = [0.42, 0, 1.0, 1.0];\r' +
                'var BEZIER_EASE_OUT = [0, 0, 0.58, 1.0];\r' +
                'var BEZIER_EASE_IN_OUT = [0.42, 0, 0.58, 1.0];\r' +
                'var BEZIER_LINEAR = [0, 0, 1.0, 1.0];\r' +
                '\r' +
                '\r' +
                'var animate1 = animateWithBezier(' + i / 15 + 0.5 +  ', 0.5, 0, 100, [0, 0, 0, 0]);\r' +
                '\r' +
                'animate1\r'
            ;

            trimPaths.property("End").expression = 
                'function getBezierX(t, p1x, p2x) {\r' +
                '    return 3 * Math.pow(1 - t, 2) * t * p1x + 3 * (1 - t) * Math.pow(t, 2) * p2x + Math.pow(t, 3);\r' +
                '}\r' +
                '\r' +
                'function getBezierY(t, p1y, p2y) {\r' +
                '    return 3 * Math.pow(1 - t, 2) * t * p1y + 3 * (1 - t) * Math.pow(t, 2) * p2y + Math.pow(t, 3);\r' +
                '}\r' +
                'function cubicBezierInternal(inputTime, x1, y1, x2, y2) {\r' +
                '    var t = Math.max(0, Math.min(1, inputTime));\r' +
                '\r' +
                '    if (t === 0) return 0;\r' +
                '    if (t === 1) return 1;\r' +
                '\r' +
                '    var start = 0.0;\r' +
                '    var end = 1.0;\r' +
                '    var u = t;\r' +
                '    var iterations = 8;\r' +
                '    for (var i = 0; i < iterations; i++) {\r' +
                '        var guessX = getBezierX(u, x1, x2);\r' +
                '        if (guessX < t) {\r' +
                '            start = u;\r' +
                '        } else {\r' +
                '            end = u;\r' +
                '        }\r' +
                '        u = (start + end) / 2;\r' +
                '    }\r' +
                '    return getBezierY(u, y1, y2);\r' +
                '}\r' +
                'function animateWithBezier(animStartTime, animDuration, startValue, endValue, bezierParams) {\r' +
                '    var x1 = bezierParams[0];\r' +
                '    var y1 = bezierParams[1];\r' +
                '    var x2 = bezierParams[2];\r' +
                '    var y2 = bezierParams[3];\r' +
                '\r' +
                '    var normalizedTime = (time - animStartTime) / animDuration;\r' +
                '\r' +
                '    var easedProgress = 0;\r' +
                '    if (time >= animStartTime && time <= animStartTime + animDuration) {\r' +
                '        easedProgress = cubicBezierInternal(normalizedTime, x1, y1, x2, y2);\r' +
                '    } else if (time > animStartTime + animDuration) {\r' +
                '        easedProgress = 1;\r' +
                '    }\r' +
                '    return startValue + (endValue - startValue) * easedProgress;\r' +
                '}\r' +
                '\r' +
                'var BEZIER_EASE = [0.25, 0.1, 0.25, 1.0];\r' +
                'var BEZIER_EASE_IN = [0.42, 0, 1.0, 1.0];\r' +
                'var BEZIER_EASE_OUT = [0, 0, 0.58, 1.0];\r' +
                'var BEZIER_EASE_IN_OUT = [0.42, 0, 0.58, 1.0];\r' +
                'var BEZIER_LINEAR = [0, 0, 1.0, 1.0];\r' +
                '\r' +
                '\r' +
                'var animate1 = animateWithBezier(' + i / 15 +  ', 0.5, 0, 100, [0, 0, 0, 0]);\r' +
                '\r' +
                'animate1\r'
            ;
        }
    }
})(convertedShapeLayer);

app.endUndoGroup();

This is my first attempt at writing an After Effects script to animate text.

However, when I run it, I consistently get the error:

strokes.property("ADBE Vector Stroke Width").setValue(1);
strokes.property("ADBE Vector Stroke Color").setValue([1, 1, 1, 1]);

These two lines show "Unable to execute script at line 51 Object is invalid".

Perhaps the same issue applies to fills as well, but trimPaths works fine.

I've tried changing the content within property(""). I referenced the After Effects scripting documentation to find the correct values, but no matter what I change, I still get the same error. What I don't understand most is why trimPaths works correctly while the others don't.

Slayemus
SlayemusCorrect answer
Participant
Jul 20, 2025 7:32 AM UTC

Thanks for your reply, I found the solution to this problem at https://ae-scripting.docsforadobe.dev/property/propertygroup/#propertygroupaddproperty 

2 replies

Dan Ebberts
Community Expert
Community Expert
Jul 20, 2025 12:17 AM UTC

Just a guess, but I think that adding a new property to groupContents may invalidate any existing references to the other properties in that group. If that's what's happening, you should be able to fix it by adding this (to refresh the object references for the stroke and fill) after your statement that adds the trim path:

strokes = groupContents.property("ADBE Vector Graphic - Stroke");
fills = groupContents.property("ADBE Vector Graphic - Fill");
Translate
Slayemus
SlayemusAuthorCorrect answer
Participant
Jul 20, 2025 7:32 AM UTC

Thanks for your reply, I found the solution to this problem at https://ae-scripting.docsforadobe.dev/property/propertygroup/#propertygroupaddproperty 

Translate