diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6e99b3e..80843a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,7 +41,8 @@ Other new features:
 Bugs fixed:
   * A point in 3d constrained to any line whose length is free no longer
     causes the line length to collapse.
-  * Lines in 3d constrained parallel are solved in a more robust way.
+  * Curve-line constraints (in 3d) and parallel constraints (in 3d)
+    are more robust.
 
 2.3
 ---
diff --git a/src/constrainteq.cpp b/src/constrainteq.cpp
index 4b6af81..398c99c 100644
--- a/src/constrainteq.cpp
+++ b/src/constrainteq.cpp
@@ -217,7 +217,8 @@ void ConstraintBase::AddEq(IdList<Equation,hEquation> *l, const ExprVector &v,
 void ConstraintBase::Generate(IdList<Param,hParam> *l) const {
     switch(type) {
         case Type::PARALLEL:
-            // Only introduce a new parameter when operating in 3d
+        case Type::CUBIC_LINE_TANGENT:
+            // Add new parameter only when we operate in 3d space
             if(workplane.v != EntityBase::FREE_IN_3D.v) break;
             // fallthrough
         case Type::PT_ON_LINE: {
@@ -720,8 +721,8 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
             ExprVector b = line->VectorGetExprs();
 
             if(workplane.v == EntityBase::FREE_IN_3D.v) {
-                AddEq(l, VectorsParallel(0, a, b), 0);
-                AddEq(l, VectorsParallel(1, a, b), 1);
+                ExprVector eq = VectorsParallel3d(a, b, h.param(0));
+                AddEq(l, eq);
             } else {
                 EntityBase *w = SK.GetEntity(workplane);
                 ExprVector wn = w->Normal()->NormalExprsN();
diff --git a/src/file.cpp b/src/file.cpp
index 7caae9f..1a16618 100644
--- a/src/file.cpp
+++ b/src/file.cpp
@@ -564,56 +564,75 @@ void SolveSpaceUI::UpgradeLegacyData() {
         }
     }
 
+    // Constraints saved in versions prior to 3.0 never had any params;
+    // version 3.0 introduced params to constraints to avoid the hairy ball problem,
+    // so force them where they belong.
     IdList<Param,hParam> oldParam = {};
     SK.param.DeepCopyInto(&oldParam);
     SS.GenerateAll(SolveSpaceUI::Generate::REGEN);
+
+    auto AllParamsExistFor = [&](const Constraint &c) {
+        IdList<Param,hParam> param = {};
+        c.Generate(&param);
+        bool allParamsExist = true;
+        for(Param &p : param) {
+            if(oldParam.FindByIdNoOops(p.h) != NULL) continue;
+            allParamsExist = false;
+            break;
+        }
+        param.Clear();
+        return allParamsExist;
+    };
+
     for(Constraint &c : SK.constraint) {
         switch(c.type) {
             case Constraint::Type::PT_ON_LINE: {
-                IdList<Param,hParam> param = {};
-                c.Generate(&param);
-                bool allParamsExist = true;
-                for(Param &p : param) {
-                    if(oldParam.FindByIdNoOops(p.h) != NULL) continue;
-                    allParamsExist = false;
-                }
-                param.Clear();
+                if(AllParamsExistFor(c)) continue;
 
-                if(!allParamsExist) {
-                    EntityBase *eln = SK.GetEntity(c.entityA);
-                    EntityBase *ea = SK.GetEntity(eln->point[0]);
-                    EntityBase *eb = SK.GetEntity(eln->point[1]);
-                    EntityBase *ep = SK.GetEntity(c.ptA);
+                EntityBase *eln = SK.GetEntity(c.entityA);
+                EntityBase *ea = SK.GetEntity(eln->point[0]);
+                EntityBase *eb = SK.GetEntity(eln->point[1]);
+                EntityBase *ep = SK.GetEntity(c.ptA);
 
-                    ExprVector exp = ep->PointGetExprsInWorkplane(c.workplane);
-                    ExprVector exa = ea->PointGetExprsInWorkplane(c.workplane);
-                    ExprVector exb = eb->PointGetExprsInWorkplane(c.workplane);
-                    ExprVector exba = exb.Minus(exa);
-                    Param *p = SK.GetParam(c.h.param(0));
-                    p->val = exba.Dot(exp.Minus(exa))->Eval() / exba.Dot(exba)->Eval();
+                ExprVector exp = ep->PointGetExprsInWorkplane(c.workplane);
+                ExprVector exa = ea->PointGetExprsInWorkplane(c.workplane);
+                ExprVector exb = eb->PointGetExprsInWorkplane(c.workplane);
+                ExprVector exba = exb.Minus(exa);
+                Param *p = SK.GetParam(c.h.param(0));
+                p->val = exba.Dot(exp.Minus(exa))->Eval() / exba.Dot(exba)->Eval();
+                break;
+            }
+
+            case Constraint::Type::CUBIC_LINE_TANGENT: {
+                if(AllParamsExistFor(c)) continue;
+
+                EntityBase *cubic = SK.GetEntity(c.entityA);
+                EntityBase *line  = SK.GetEntity(c.entityB);
+
+                ExprVector a;
+                if(c.other) {
+                    a = cubic->CubicGetFinishTangentExprs();
+                } else {
+                    a = cubic->CubicGetStartTangentExprs();
                 }
+
+                ExprVector b = line->VectorGetExprs();
+
+                Param *param = SK.GetParam(c.h.param(0));
+                param->val = a.Dot(b)->Eval() / b.Dot(b)->Eval();
                 break;
             }
 
             case Constraint::Type::PARALLEL: {
-                IdList<Param,hParam> param = {};
-                c.Generate(&param);
-                bool allParamsExist = true;
-                for(Param &p : param) {
-                    if(oldParam.FindByIdNoOops(p.h) != NULL) continue;
-                    allParamsExist = false;
-                }
-                param.Clear();
+                if(AllParamsExistFor(c)) continue;
 
-                if(!allParamsExist) {
-                    EntityBase *ea = SK.GetEntity(c.entityA),
-                               *eb = SK.GetEntity(c.entityB);
-                    ExprVector a = ea->VectorGetExprsInWorkplane(c.workplane);
-                    ExprVector b = eb->VectorGetExprsInWorkplane(c.workplane);
+                EntityBase *ea = SK.GetEntity(c.entityA),
+                           *eb = SK.GetEntity(c.entityB);
+                ExprVector a = ea->VectorGetExprsInWorkplane(c.workplane);
+                ExprVector b = eb->VectorGetExprsInWorkplane(c.workplane);
 
-                    Param *param = SK.GetParam(c.h.param(0));
-                    param->val = a.Dot(b)->Eval() / b.Dot(b)->Eval();
-                }
+                Param *param = SK.GetParam(c.h.param(0));
+                param->val = a.Dot(b)->Eval() / b.Dot(b)->Eval();
                 break;
             }
 
diff --git a/test/constraint/cubic_line_tangent/free_in_3d.slvs b/test/constraint/cubic_line_tangent/free_in_3d.slvs
index a20030d..19f9658 100644
--- a/test/constraint/cubic_line_tangent/free_in_3d.slvs
+++ b/test/constraint/cubic_line_tangent/free_in_3d.slvs
@@ -180,6 +180,10 @@ AddParam
 Param.h.v.=00050015
 AddParam
 
+Param.h.v.=40000002
+Param.val=-1.00000000000000000000
+AddParam
+
 Request.h.v=00000001
 Request.type=100
 Request.group.v=00000001