Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] None value assignment in structs not handled properly causing indefinite hanging in terminal #7249

Open
rickymagner opened this issue Nov 7, 2023 · 2 comments

Comments

@rickymagner
Copy link

This is a bug report that seems to be caused by the None type introduced in version 1.1 not being handled properly when assigned to fields in custom structs. Consider the following minimal example:

version development

struct OptionalFiles {
	File required
	File? optional
}

workflow TestNone {
	input {
		Array[File] letters = ["a.txt", "b.txt", "c.txt"]
	}

	scatter (letter in letters) {
		OptionalFiles opt = {"required": letter, "optional": None}
	}

	output {
		Array[OptionalFiles] out = opt
	}
}

This is run in a directory containing empty files a.txt, b.txt, and c.txt. Assigning None to the optional field in type OptionalFiles seems to cause the following unhandled stacktrace:

[2023-11-07 14:51:14,22] [info] MaterializeWorkflowDescriptorActor [4e522458]: Call-to-Backend assignments:
[2023-11-07 14:51:17,38] [error] Cannot construct WomMapType(WomStringType,WomOptionalType(WomAnyType)) with mixed types in map values: [WomOptionalValue(WomSingleFileType,Some(WomSingleFile(c.txt))), WomOptionalValue(WomAnyType,None)]
java.lang.UnsupportedOperationException: Cannot construct WomMapType(WomStringType,WomOptionalType(WomAnyType)) with mixed types in map values: [WomOptionalValue(WomSingleFileType,Some(WomSingleFile(c.txt))), WomOptionalValue(WomAnyType,None)]
	at wom.values.WomMap.<init>(WomMap.scala:65)
	at wom.values.WomMap$.apply(WomMap.scala:50)
	at wom.values.WomMap$.coerceMap(WomMap.scala:30)
	at wom.values.WomMap$.apply(WomMap.scala:46)
	at wdl.transforms.base.linking.expression.values.LiteralEvaluators$$anon$5.$anonfun$evaluateValue$12(LiteralEvaluators.scala:89)
	at cats.data.Validated.map(Validated.scala:559)
	at wdl.transforms.base.linking.expression.values.LiteralEvaluators$$anon$5.evaluateValue(LiteralEvaluators.scala:86)
	at wdl.transforms.base.linking.expression.values.LiteralEvaluators$$anon$5.evaluateValue(LiteralEvaluators.scala:74)
	at wdl.model.draft3.graph.expression.ValueEvaluator$Ops.evaluateValue(ValueEvaluator.scala:10)
	at wdl.model.draft3.graph.expression.ValueEvaluator$Ops.evaluateValue$(ValueEvaluator.scala:10)
	at wdl.model.draft3.graph.expression.ValueEvaluator$ops$$anon$1.evaluateValue(ValueEvaluator.scala:10)
	at wdl.transforms.biscayne.linking.expression.values.package$$anon$1.evaluateValue(values.scala:38)
	at wdl.transforms.biscayne.linking.expression.values.package$$anon$1.evaluateValue(values.scala:23)
	at wdl.model.draft3.graph.expression.ValueEvaluator$Ops.evaluateValue(ValueEvaluator.scala:10)
	at wdl.model.draft3.graph.expression.ValueEvaluator$Ops.evaluateValue$(ValueEvaluator.scala:10)
	at wdl.model.draft3.graph.expression.ValueEvaluator$ops$$anon$1.evaluateValue(ValueEvaluator.scala:10)
	at wdl.transforms.base.wdlom2wom.expression.WdlomWomExpression.evaluateValue(WdlomWomExpression.scala:32)
	at wom.graph.expression.ExpressionNode.evaluateAndCoerce(ExpressionNode.scala:33)
	at wom.graph.expression.ExpressionNode.$anonfun$evaluate$2(ExpressionNode.scala:41)
	at scala.util.Either.flatMap(Either.scala:352)
	at wom.graph.expression.ExpressionNode.evaluate(ExpressionNode.scala:40)
	at cromwell.engine.workflow.lifecycle.execution.keys.ExpressionKey.processRunnable(ExpressionKey.scala:25)
	at cromwell.engine.workflow.lifecycle.execution.WorkflowExecutionActor.$anonfun$startRunnableNodes$7(WorkflowExecutionActor.scala:563)
	at scala.collection.immutable.List.map(List.scala:246)
	at cromwell.engine.workflow.lifecycle.execution.WorkflowExecutionActor.cromwell$engine$workflow$lifecycle$execution$WorkflowExecutionActor$$startRunnableNodes(WorkflowExecutionActor.scala:557)
	at cromwell.engine.workflow.lifecycle.execution.WorkflowExecutionActor$$anonfun$5.applyOrElse(WorkflowExecutionActor.scala:211)
	at cromwell.engine.workflow.lifecycle.execution.WorkflowExecutionActor$$anonfun$5.applyOrElse(WorkflowExecutionActor.scala:209)
	at scala.PartialFunction$OrElse.apply(PartialFunction.scala:266)
	at akka.actor.FSM.processEvent(FSM.scala:710)
	at akka.actor.FSM.processEvent$(FSM.scala:704)
	at cromwell.engine.workflow.lifecycle.execution.WorkflowExecutionActor.akka$actor$LoggingFSM$$super$processEvent(WorkflowExecutionActor.scala:54)
	at akka.actor.LoggingFSM.processEvent(FSM.scala:847)
	at akka.actor.LoggingFSM.processEvent$(FSM.scala:829)
	at cromwell.engine.workflow.lifecycle.execution.WorkflowExecutionActor.processEvent(WorkflowExecutionActor.scala:54)
	at akka.actor.FSM.akka$actor$FSM$$processMsg(FSM.scala:701)
	at akka.actor.FSM$$anonfun$receive$1.applyOrElse(FSM.scala:695)
	at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:35)
	at cromwell.engine.workflow.lifecycle.execution.WorkflowExecutionActor$$anonfun$receive$1.applyOrElse(WorkflowExecutionActor.scala:507)
	at akka.actor.Actor.aroundReceive(Actor.scala:539)
	at akka.actor.Actor.aroundReceive$(Actor.scala:537)
	at cromwell.engine.workflow.lifecycle.execution.WorkflowExecutionActor.akka$actor$Timers$$super$aroundReceive(WorkflowExecutionActor.scala:54)
	at akka.actor.Timers.aroundReceive(Timers.scala:51)
	at akka.actor.Timers.aroundReceive$(Timers.scala:40)
	at cromwell.engine.workflow.lifecycle.execution.WorkflowExecutionActor.aroundReceive(WorkflowExecutionActor.scala:54)
	at akka.actor.ActorCell.receiveMessage(ActorCell.scala:614)
	at akka.actor.ActorCell.invoke(ActorCell.scala:583)
	at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:268)
	at akka.dispatch.Mailbox.run(Mailbox.scala:229)
	at akka.dispatch.Mailbox.exec(Mailbox.scala:241)
	at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
	at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
	at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
	at akka.dispatch.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
[2023-11-07 14:51:17,39] [info] Message [cromwell.engine.workflow.lifecycle.EngineLifecycleActorAbortCommand$] from Actor[akka://cromwell-system/user/SingleWorkflowRunnerActor/WorkflowManagerActor/WorkflowActor-4e522458-e360-45e8-be15-2fc99652d692#-686070856] to Actor[akka://cromwell-system/user/SingleWorkflowRunnerActor/WorkflowManagerActor/WorkflowActor-4e522458-e360-45e8-be15-2fc99652d692/WorkflowExecutionActor-4e522458-e360-45e8-be15-2fc99652d692#-1420206102] was not delivered. [1] dead letters encountered, no more dead letters will be logged. If this is not an expected behavior, then [Actor[akka://cromwell-system/user/SingleWorkflowRunnerActor/WorkflowManagerActor/WorkflowActor-4e522458-e360-45e8-be15-2fc99652d692/WorkflowExecutionActor-4e522458-e360-45e8-be15-2fc99652d692#-1420206102]] may have terminated unexpectedly, This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.

In fact, the program becomes unresponsive to even a Ctrl+C kill command and I have to close the terminal entirely to stop it.

The WDL passes womtool validate (version 84) and was run using Cromwell version 84.

When run in Terra, the workflow just immediate goes into an aborting state without any helpful error message. It would be great to incorporate this type of support for None inside struct fields.

@aofarrel
Copy link

aofarrel commented Dec 7, 2023

Cromwell does not support WDL 1.1 at the moment, although I think there may still be an underlying bug here -- I have seen odd behavior in Cromwell using Structs in WDL 1.0 where the contents of the struct may be undefined.

If you want to use WDL 1.1, I recommend miniwdl as an alternative to Cromwell. If you need to use Cromwell (eg you need to use Terra), I recommend avoiding any optional types when using structs through some combination of select_first() and only interacting with your optional file if length(some_array_with_optional_in_it) > 0. You can also use defined(), but be aware that defined(output_of_task_that_did_not_run) can be true in some cases.

@rickymagner
Copy link
Author

Hi, thanks for the reply. I was using version development which I had assumed meant it had at least the 1.1 features (including None). I agree using some safety checks and select_first can work, but often using the above paradigm with None can be a lot clearer/easier to work with in code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants