[{"data":1,"prerenderedAt":2159},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Fhandle-processing-feedback-and-errors-pyqgis":3},{"id":4,"title":5,"body":6,"description":2148,"extension":2149,"meta":2150,"navigation":261,"path":2155,"seo":2156,"stem":2157,"__hash__":2158},"docs\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Fhandle-processing-feedback-and-errors-pyqgis\u002Findex.md","Handle Processing Feedback and Errors in PyQGIS",{"type":7,"value":8,"toc":2133},"minimark",[9,13,21,30,195,200,222,226,235,338,344,348,354,639,662,666,850,855,967,971,974,1226,1247,1251,1254,1637,1646,1649,1653,1656,1670,1676,1683,1776,1788,1792,1798,1854,1862,1866,1872,1970,1974,2025,2029,2036,2040,2046,2055,2073,2095,2099,2129],[10,11,5],"h1",{"id":12},"handle-processing-feedback-and-errors-in-pyqgis",[14,15,16,20],"p",{},[17,18,19],"code",{},"processing.run()"," looks like an ordinary function call, and it is not quite one. It can succeed while writing nothing, warn about something important without stopping, and report a failure through a channel your script never reads. In an interactive console none of that matters much. In a chain of eight algorithms running unattended at three in the morning, it is the difference between a fixable error and a directory of silently wrong outputs.",[14,22,23,24,29],{},"This recipe belongs to ",[25,26,28],"a",{"href":27},"\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002F","Chaining Processing Algorithms in PyQGIS",". It covers the feedback object, subclassing it to route messages where you want them, making failures raise, adding progress and cancellation to a long job, and the difference between an algorithm error and a Python exception.",[14,31,32],{},[33,34,39,43,47,54,71,80,88,92,97,104,107,111,115,124,130,135,139,145,149,152,156,159,163,166,168,172,176,179,183,191],"svg",{"viewBox":35,"role":36,"ariaLabel":37,"xmlns":38},"0 0 760 316","img","The channels an algorithm reports through: return value, feedback messages at several severities, exceptions, and the log, with only some of them reaching a script that ignores feedback","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[40,41,42],"title",{},"Four channels, and the two most scripts read",[44,45,46],"desc",{},"An algorithm reports through its return dictionary, through feedback messages at info, warning and error severities, and through exceptions. A script that passes no feedback object sees only the return value and any exception, so warnings and non fatal errors pass unnoticed.",[48,49],"rect",{"x":50,"y":50,"width":51,"height":52,"fill":53},"0","760","316","#f6f3ea",[55,56,57],"defs",{},[58,59,66],"marker",{"id":60,"viewBox":61,"refX":62,"refY":63,"markerWidth":64,"markerHeight":64,"orient":65},"fbArrow","0 0 10 10","8","5","7","auto-start-reverse",[67,68],"path",{"d":69,"fill":70},"M0 0 L10 5 L0 10 z","#2f3b35",[72,73,79],"text",{"x":74,"y":75,"style":76,"fill":77,"textAnchor":78},"380","28","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","Two of these reach a script that passes no feedback",[48,81],{"x":82,"y":83,"width":84,"height":83,"rx":62,"fill":85,"stroke":86,"style":87},"290","52","180","#eef7f4","#0f766e","stroke-width:2.5",[72,89,19],{"x":74,"y":90,"style":91,"fill":86,"textAnchor":78},"72","text-anchor:middle;font-size:11px;font-weight:bold;font-family:monospace",[72,93,96],{"x":74,"y":94,"style":95,"fill":70,"textAnchor":78},"92","text-anchor:middle;font-size:10px;font-family:sans-serif","one algorithm",[98,99],"line",{"x1":100,"y1":101,"x2":84,"y2":102,"stroke":70,"style":103},"330","104","140","stroke-width:2;marker-end:url(#fbArrow)",[98,105],{"x1":106,"y1":101,"x2":100,"y2":102,"stroke":70,"style":103},"360",[98,108],{"x1":109,"y1":101,"x2":110,"y2":102,"stroke":70,"style":103},"400","440",[98,112],{"x1":113,"y1":101,"x2":114,"y2":102,"stroke":70,"style":103},"430","590",[48,116],{"x":117,"y":118,"width":119,"height":120,"rx":121,"fill":122,"stroke":123,"style":87},"24","146","200","94","10","#edf8e9","#15803d",[72,125,129],{"x":126,"y":127,"style":128,"fill":123,"textAnchor":78},"124","172","text-anchor:middle;font-size:10.5px;font-weight:bold;font-family:sans-serif","return dict",[72,131,134],{"x":126,"y":132,"style":133,"fill":70,"textAnchor":78},"194","text-anchor:middle;font-size:10px;font-family:monospace","OUTPUT: path",[72,136,138],{"x":126,"y":137,"style":95,"fill":123,"textAnchor":78},"216","always visible",[48,140],{"x":141,"y":118,"width":142,"height":120,"rx":121,"fill":143,"stroke":144,"style":87},"240","176","#fdf2e2","#b45309",[72,146,148],{"x":147,"y":127,"style":128,"fill":144,"textAnchor":78},"328","pushWarning",[72,150,151],{"x":147,"y":132,"style":95,"fill":70,"textAnchor":78},"\"3 features skipped\"",[72,153,155],{"x":147,"y":137,"style":95,"fill":154,"textAnchor":78},"#b91c1c","lost without feedback",[48,157],{"x":158,"y":118,"width":142,"height":120,"rx":121,"fill":143,"stroke":144,"style":87},"432",[72,160,162],{"x":161,"y":127,"style":128,"fill":144,"textAnchor":78},"520","reportError",[72,164,165],{"x":161,"y":132,"style":95,"fill":70,"textAnchor":78},"non-fatal by default",[72,167,155],{"x":161,"y":137,"style":95,"fill":154,"textAnchor":78},[48,169],{"x":170,"y":118,"width":171,"height":120,"rx":121,"fill":122,"stroke":123,"style":87},"624","112",[72,173,175],{"x":174,"y":127,"style":128,"fill":123,"textAnchor":78},"680","exception",[72,177,178],{"x":174,"y":132,"style":95,"fill":70,"textAnchor":78},"QgsProcessing",[72,180,182],{"x":174,"y":181,"style":95,"fill":70,"textAnchor":78},"212","Exception",[48,184],{"x":185,"y":186,"width":187,"height":188,"rx":62,"fill":189,"stroke":86,"style":190},"150","264","460","42","#fffdf7","stroke-width:2",[72,192,194],{"x":74,"y":82,"style":193,"fill":70,"textAnchor":78},"text-anchor:middle;font-size:10.5px;font-family:sans-serif","a feedback object is the only way to see the middle two",[196,197,199],"h2",{"id":198},"prerequisites","Prerequisites",[201,202,203,211,214],"ul",{},[204,205,206,210],"li",{},[207,208,209],"strong",{},"QGIS 3.34 LTR"," (bundled Python 3.12) or newer with the Processing framework initialised.",[204,212,213],{},"A chain worth instrumenting; a single fast algorithm needs none of this.",[204,215,216,217,221],{},"For headless use, Processing initialised explicitly — see ",[25,218,220],{"href":219},"\u002Fpyqgis-fundamentals-environment-setup\u002Fvirtual-environments-for-gis\u002Frunning-python-scripts-outside-qgis-desktop\u002F","running scripts outside QGIS Desktop",".",[196,223,225],{"id":224},"the-default-is-quieter-than-you-think","The default is quieter than you think",[14,227,228,230,231,234],{},[17,229,19],{}," raises ",[17,232,233],{},"QgsProcessingException"," when an algorithm fails outright. It does not raise for a warning, and it does not raise when an algorithm decides a bad input is survivable and continues with fewer features.",[236,237,242],"pre",{"className":238,"code":239,"language":240,"meta":241,"style":241},"language-python shiki shiki-themes github-dark","import processing\n\nresult = processing.run(\"native:buffer\", {\n    \"INPUT\": \"\u002Fdata\u002Fparcels.gpkg\",\n    \"DISTANCE\": 10,\n    \"OUTPUT\": \"\u002Fdata\u002Foutput\u002Fbuffered.gpkg\",\n})\nprint(result)\n","python","",[17,243,244,256,263,282,297,310,323,329],{"__ignoreMap":241},[245,246,248,252],"span",{"class":98,"line":247},1,[245,249,251],{"class":250},"snl16","import",[245,253,255],{"class":254},"s95oV"," processing\n",[245,257,259],{"class":98,"line":258},2,[245,260,262],{"emptyLinePlaceholder":261},true,"\n",[245,264,266,269,272,275,279],{"class":98,"line":265},3,[245,267,268],{"class":254},"result ",[245,270,271],{"class":250},"=",[245,273,274],{"class":254}," processing.run(",[245,276,278],{"class":277},"sU2Wk","\"native:buffer\"",[245,280,281],{"class":254},", {\n",[245,283,285,288,291,294],{"class":98,"line":284},4,[245,286,287],{"class":277},"    \"INPUT\"",[245,289,290],{"class":254},": ",[245,292,293],{"class":277},"\"\u002Fdata\u002Fparcels.gpkg\"",[245,295,296],{"class":254},",\n",[245,298,300,303,305,308],{"class":98,"line":299},5,[245,301,302],{"class":277},"    \"DISTANCE\"",[245,304,290],{"class":254},[245,306,121],{"class":307},"sDLfK",[245,309,296],{"class":254},[245,311,313,316,318,321],{"class":98,"line":312},6,[245,314,315],{"class":277},"    \"OUTPUT\"",[245,317,290],{"class":254},[245,319,320],{"class":277},"\"\u002Fdata\u002Foutput\u002Fbuffered.gpkg\"",[245,322,296],{"class":254},[245,324,326],{"class":98,"line":325},7,[245,327,328],{"class":254},"})\n",[245,330,332,335],{"class":98,"line":331},8,[245,333,334],{"class":307},"print",[245,336,337],{"class":254},"(result)\n",[14,339,340,343],{},[207,341,342],{},"Breakdown:"," With no feedback object the messages go to a default instance that discards them. If three features had invalid geometries and were skipped, that is a warning nobody sees, and the output has three fewer rows than the input. Comparing feature counts across a chain is a cheap defence, but reading the warnings is a better one.",[196,345,347],{"id":346},"a-feedback-object-that-routes-messages","A feedback object that routes messages",[14,349,350,353],{},[17,351,352],{},"QgsProcessingFeedback"," has virtual methods for each severity, and subclassing it puts the messages wherever they belong.",[236,355,357],{"className":238,"code":356,"language":240,"meta":241,"style":241},"from qgis.core import QgsProcessingFeedback, QgsMessageLog, Qgis\n\n\nclass LoggingFeedback(QgsProcessingFeedback):\n    def __init__(self, tag=\"Processing\", strict=False):\n        super().__init__()\n        self.tag = tag\n        self.strict = strict\n        self.warnings = []\n        self.errors = []\n\n    def pushInfo(self, info):\n        QgsMessageLog.logMessage(info, self.tag, Qgis.Info)\n\n    def pushWarning(self, warning):\n        self.warnings.append(warning)\n        QgsMessageLog.logMessage(warning, self.tag, Qgis.Warning)\n\n    def reportError(self, error, fatalError=False):\n        self.errors.append(error)\n        QgsMessageLog.logMessage(error, self.tag, Qgis.Critical)\n        if self.strict:\n            raise RuntimeError(error)\n\n    def setProgress(self, progress):\n        super().setProgress(progress)\n",[17,358,359,372,376,380,397,423,437,450,462,475,487,492,503,515,520,531,539,550,555,572,580,591,603,615,620,631],{"__ignoreMap":241},[245,360,361,364,367,369],{"class":98,"line":247},[245,362,363],{"class":250},"from",[245,365,366],{"class":254}," qgis.core ",[245,368,251],{"class":250},[245,370,371],{"class":254}," QgsProcessingFeedback, QgsMessageLog, Qgis\n",[245,373,374],{"class":98,"line":258},[245,375,262],{"emptyLinePlaceholder":261},[245,377,378],{"class":98,"line":265},[245,379,262],{"emptyLinePlaceholder":261},[245,381,382,385,389,392,394],{"class":98,"line":284},[245,383,384],{"class":250},"class",[245,386,388],{"class":387},"svObZ"," LoggingFeedback",[245,390,391],{"class":254},"(",[245,393,352],{"class":387},[245,395,396],{"class":254},"):\n",[245,398,399,402,405,408,410,413,416,418,421],{"class":98,"line":299},[245,400,401],{"class":250},"    def",[245,403,404],{"class":307}," __init__",[245,406,407],{"class":254},"(self, tag",[245,409,271],{"class":250},[245,411,412],{"class":277},"\"Processing\"",[245,414,415],{"class":254},", strict",[245,417,271],{"class":250},[245,419,420],{"class":307},"False",[245,422,396],{"class":254},[245,424,425,428,431,434],{"class":98,"line":312},[245,426,427],{"class":307},"        super",[245,429,430],{"class":254},"().",[245,432,433],{"class":307},"__init__",[245,435,436],{"class":254},"()\n",[245,438,439,442,445,447],{"class":98,"line":325},[245,440,441],{"class":307},"        self",[245,443,444],{"class":254},".tag ",[245,446,271],{"class":250},[245,448,449],{"class":254}," tag\n",[245,451,452,454,457,459],{"class":98,"line":331},[245,453,441],{"class":307},[245,455,456],{"class":254},".strict ",[245,458,271],{"class":250},[245,460,461],{"class":254}," strict\n",[245,463,465,467,470,472],{"class":98,"line":464},9,[245,466,441],{"class":307},[245,468,469],{"class":254},".warnings ",[245,471,271],{"class":250},[245,473,474],{"class":254}," []\n",[245,476,478,480,483,485],{"class":98,"line":477},10,[245,479,441],{"class":307},[245,481,482],{"class":254},".errors ",[245,484,271],{"class":250},[245,486,474],{"class":254},[245,488,490],{"class":98,"line":489},11,[245,491,262],{"emptyLinePlaceholder":261},[245,493,495,497,500],{"class":98,"line":494},12,[245,496,401],{"class":250},[245,498,499],{"class":387}," pushInfo",[245,501,502],{"class":254},"(self, info):\n",[245,504,506,509,512],{"class":98,"line":505},13,[245,507,508],{"class":254},"        QgsMessageLog.logMessage(info, ",[245,510,511],{"class":307},"self",[245,513,514],{"class":254},".tag, Qgis.Info)\n",[245,516,518],{"class":98,"line":517},14,[245,519,262],{"emptyLinePlaceholder":261},[245,521,523,525,528],{"class":98,"line":522},15,[245,524,401],{"class":250},[245,526,527],{"class":387}," pushWarning",[245,529,530],{"class":254},"(self, warning):\n",[245,532,534,536],{"class":98,"line":533},16,[245,535,441],{"class":307},[245,537,538],{"class":254},".warnings.append(warning)\n",[245,540,542,545,547],{"class":98,"line":541},17,[245,543,544],{"class":254},"        QgsMessageLog.logMessage(warning, ",[245,546,511],{"class":307},[245,548,549],{"class":254},".tag, Qgis.Warning)\n",[245,551,553],{"class":98,"line":552},18,[245,554,262],{"emptyLinePlaceholder":261},[245,556,558,560,563,566,568,570],{"class":98,"line":557},19,[245,559,401],{"class":250},[245,561,562],{"class":387}," reportError",[245,564,565],{"class":254},"(self, error, fatalError",[245,567,271],{"class":250},[245,569,420],{"class":307},[245,571,396],{"class":254},[245,573,575,577],{"class":98,"line":574},20,[245,576,441],{"class":307},[245,578,579],{"class":254},".errors.append(error)\n",[245,581,583,586,588],{"class":98,"line":582},21,[245,584,585],{"class":254},"        QgsMessageLog.logMessage(error, ",[245,587,511],{"class":307},[245,589,590],{"class":254},".tag, Qgis.Critical)\n",[245,592,594,597,600],{"class":98,"line":593},22,[245,595,596],{"class":250},"        if",[245,598,599],{"class":307}," self",[245,601,602],{"class":254},".strict:\n",[245,604,606,609,612],{"class":98,"line":605},23,[245,607,608],{"class":250},"            raise",[245,610,611],{"class":307}," RuntimeError",[245,613,614],{"class":254},"(error)\n",[245,616,618],{"class":98,"line":617},24,[245,619,262],{"emptyLinePlaceholder":261},[245,621,623,625,628],{"class":98,"line":622},25,[245,624,401],{"class":250},[245,626,627],{"class":387}," setProgress",[245,629,630],{"class":254},"(self, progress):\n",[245,632,634,636],{"class":98,"line":633},26,[245,635,427],{"class":307},[245,637,638],{"class":254},"().setProgress(progress)\n",[14,640,641,643,644,646,647,650,651,654,655,658,659,221],{},[207,642,342],{}," ",[17,645,162],{}," receives a ",[17,648,649],{},"fatalError"," flag that the algorithm sets when it is about to give up; a non-fatal error means \"something went wrong and I carried on\", which is precisely the case worth converting into a raise in an unattended run. Collecting the messages into lists as well as logging them lets the calling code decide afterwards — ",[17,652,653],{},"if feedback.warnings: ..."," is often better than raising mid-run, because it lets the chain finish and report everything at once. Calling ",[17,656,657],{},"super().setProgress()"," matters if anything else is reading ",[17,660,661],{},"feedback.progress()",[196,663,665],{"id":664},"use-it-and-act-on-what-it-collected","Use it, and act on what it collected",[236,667,669],{"className":238,"code":668,"language":240,"meta":241,"style":241},"feedback = LoggingFeedback(tag=\"Nightly build\")\n\nresult = processing.run(\n    \"native:buffer\",\n    {\"INPUT\": \"\u002Fdata\u002Fparcels.gpkg\", \"DISTANCE\": 10,\n     \"OUTPUT\": \"\u002Fdata\u002Foutput\u002Fbuffered.gpkg\"},\n    feedback=feedback,\n)\n\nif feedback.errors:\n    raise RuntimeError(f\"{len(feedback.errors)} error(s): {feedback.errors[0]}\")\nif feedback.warnings:\n    print(f\"completed with {len(feedback.warnings)} warning(s)\")\n",[17,670,671,693,697,706,713,737,749,759,763,767,775,819,826],{"__ignoreMap":241},[245,672,673,676,678,681,685,687,690],{"class":98,"line":247},[245,674,675],{"class":254},"feedback ",[245,677,271],{"class":250},[245,679,680],{"class":254}," LoggingFeedback(",[245,682,684],{"class":683},"s9osk","tag",[245,686,271],{"class":250},[245,688,689],{"class":277},"\"Nightly build\"",[245,691,692],{"class":254},")\n",[245,694,695],{"class":98,"line":258},[245,696,262],{"emptyLinePlaceholder":261},[245,698,699,701,703],{"class":98,"line":265},[245,700,268],{"class":254},[245,702,271],{"class":250},[245,704,705],{"class":254}," processing.run(\n",[245,707,708,711],{"class":98,"line":284},[245,709,710],{"class":277},"    \"native:buffer\"",[245,712,296],{"class":254},[245,714,715,718,721,723,725,728,731,733,735],{"class":98,"line":299},[245,716,717],{"class":254},"    {",[245,719,720],{"class":277},"\"INPUT\"",[245,722,290],{"class":254},[245,724,293],{"class":277},[245,726,727],{"class":254},", ",[245,729,730],{"class":277},"\"DISTANCE\"",[245,732,290],{"class":254},[245,734,121],{"class":307},[245,736,296],{"class":254},[245,738,739,742,744,746],{"class":98,"line":312},[245,740,741],{"class":277},"     \"OUTPUT\"",[245,743,290],{"class":254},[245,745,320],{"class":277},[245,747,748],{"class":254},"},\n",[245,750,751,754,756],{"class":98,"line":325},[245,752,753],{"class":683},"    feedback",[245,755,271],{"class":250},[245,757,758],{"class":254},"feedback,\n",[245,760,761],{"class":98,"line":331},[245,762,692],{"class":254},[245,764,765],{"class":98,"line":464},[245,766,262],{"emptyLinePlaceholder":261},[245,768,769,772],{"class":98,"line":477},[245,770,771],{"class":250},"if",[245,773,774],{"class":254}," feedback.errors:\n",[245,776,777,780,782,784,787,790,793,796,799,802,805,808,810,813,815,817],{"class":98,"line":489},[245,778,779],{"class":250},"    raise",[245,781,611],{"class":307},[245,783,391],{"class":254},[245,785,786],{"class":250},"f",[245,788,789],{"class":277},"\"",[245,791,792],{"class":307},"{len",[245,794,795],{"class":254},"(feedback.errors)",[245,797,798],{"class":307},"}",[245,800,801],{"class":277}," error(s): ",[245,803,804],{"class":307},"{",[245,806,807],{"class":254},"feedback.errors[",[245,809,50],{"class":307},[245,811,812],{"class":254},"]",[245,814,798],{"class":307},[245,816,789],{"class":277},[245,818,692],{"class":254},[245,820,821,823],{"class":98,"line":494},[245,822,771],{"class":250},[245,824,825],{"class":254}," feedback.warnings:\n",[245,827,828,831,833,835,838,840,843,845,848],{"class":98,"line":505},[245,829,830],{"class":307},"    print",[245,832,391],{"class":254},[245,834,786],{"class":250},[245,836,837],{"class":277},"\"completed with ",[245,839,792],{"class":307},[245,841,842],{"class":254},"(feedback.warnings)",[245,844,798],{"class":307},[245,846,847],{"class":277}," warning(s)\"",[245,849,692],{"class":254},[14,851,852,854],{},[207,853,342],{}," Passing the same feedback object through every step of a chain accumulates the whole run's messages in one place, which is what you want in a log. Passing a fresh one per step and checking after each is what you want when a later step should not run after an earlier warning. Both are reasonable; mixing them by accident is not, so decide once per script.",[14,856,857],{},[33,858,861,864,867,870,877,880,885,889,896,900,903,906,908,911,914,918,923,927,931,934,938,942,946,949,952,957,960,962,964],{"viewBox":859,"role":36,"ariaLabel":860,"xmlns":38},"0 0 760 288","A chain of algorithms sharing one feedback object so all messages accumulate, compared with a fresh feedback object per step so each step can be checked before the next runs",[40,862,863],{},"One feedback for the chain, or one per step",[44,865,866],{},"Sharing a single feedback object across a chain collects every message into one report at the end, which suits a log. Creating a fresh object per step allows the chain to stop as soon as a step reports a problem, which suits a pipeline where a later step would compound the error.",[48,868],{"x":50,"y":50,"width":51,"height":869,"fill":53},"288",[55,871,872],{},[58,873,875],{"id":874,"viewBox":61,"refX":62,"refY":63,"markerWidth":64,"markerHeight":64,"orient":65},"chArrow",[67,876],{"d":69,"fill":70},[72,878,879],{"x":74,"y":75,"style":76,"fill":77,"textAnchor":78},"Decide once whether a warning should stop the chain",[48,881],{"x":117,"y":83,"width":882,"height":120,"rx":121,"fill":883,"stroke":884,"style":87},"712","#eff3ff","#2563eb",[72,886,888],{"x":171,"y":887,"style":128,"fill":884,"textAnchor":78},"80","shared",[48,890],{"x":84,"y":891,"width":892,"height":893,"rx":63,"fill":894,"stroke":884,"style":895},"88","120","40","#dbeafe","stroke-width:1.6",[72,897,899],{"x":141,"y":171,"style":898,"fill":70,"textAnchor":78},"text-anchor:middle;font-size:9.5px;font-family:sans-serif","buffer",[48,901],{"x":902,"y":891,"width":892,"height":893,"rx":63,"fill":894,"stroke":884,"style":895},"320",[72,904,905],{"x":74,"y":171,"style":898,"fill":70,"textAnchor":78},"clip",[48,907],{"x":187,"y":891,"width":892,"height":893,"rx":63,"fill":894,"stroke":884,"style":895},[72,909,910],{"x":161,"y":171,"style":898,"fill":70,"textAnchor":78},"dissolve",[48,912],{"x":913,"y":891,"width":892,"height":893,"rx":63,"fill":189,"stroke":884,"style":895},"600",[72,915,917],{"x":916,"y":171,"style":898,"fill":70,"textAnchor":78},"660","one report",[98,919],{"x1":920,"y1":921,"x2":52,"y2":921,"stroke":70,"style":922},"304","108","stroke-width:1.8;marker-end:url(#chArrow)",[98,924],{"x1":925,"y1":921,"x2":926,"y2":921,"stroke":70,"style":922},"444","456",[98,928],{"x1":929,"y1":921,"x2":930,"y2":921,"stroke":70,"style":922},"584","596",[48,932],{"x":117,"y":933,"width":882,"height":120,"rx":121,"fill":122,"stroke":123,"style":87},"162",[72,935,937],{"x":171,"y":936,"style":128,"fill":123,"textAnchor":78},"190","per step",[48,939],{"x":84,"y":940,"width":892,"height":893,"rx":63,"fill":941,"stroke":123,"style":895},"198","#e8efe6",[72,943,945],{"x":141,"y":944,"style":898,"fill":70,"textAnchor":78},"222","buffer ✓",[48,947],{"x":902,"y":940,"width":892,"height":893,"rx":63,"fill":143,"stroke":154,"style":948},"stroke-width:1.8",[72,950,951],{"x":74,"y":944,"style":898,"fill":70,"textAnchor":78},"clip ⚠ stop",[48,953],{"x":187,"y":940,"width":892,"height":893,"rx":63,"fill":954,"stroke":955,"style":956},"#efeadd","#59645f","stroke-width:1.4;stroke-dasharray:4 3",[72,958,959],{"x":161,"y":944,"style":898,"fill":955,"textAnchor":78},"not run",[48,961],{"x":913,"y":940,"width":892,"height":893,"rx":63,"fill":954,"stroke":955,"style":956},[72,963,959],{"x":916,"y":944,"style":898,"fill":955,"textAnchor":78},[98,965],{"x1":920,"y1":966,"x2":52,"y2":966,"stroke":70,"style":922},"218",[196,968,970],{"id":969},"progress-and-cancellation","Progress and cancellation",[14,972,973],{},"The same object carries progress reporting and a cancellation flag, which is what makes a long chain usable inside a plugin.",[236,975,977],{"className":238,"code":976,"language":240,"meta":241,"style":241},"class ChainFeedback(LoggingFeedback):\n    def __init__(self, steps, dialog=None, **kwargs):\n        super().__init__(**kwargs)\n        self.steps = steps\n        self.step = 0\n        self.dialog = dialog\n\n    def next_step(self, name):\n        self.step += 1\n        self.pushInfo(f\"[{self.step}\u002F{self.steps}] {name}\")\n\n    def setProgress(self, progress):\n        overall = ((self.step - 1) + progress \u002F 100.0) \u002F self.steps * 100.0\n        if self.dialog:\n            self.dialog.setValue(int(overall))\n        super().setProgress(overall)\n",[17,978,979,993,1015,1030,1042,1054,1066,1070,1080,1092,1136,1140,1148,1196,1205,1219],{"__ignoreMap":241},[245,980,981,983,986,988,991],{"class":98,"line":247},[245,982,384],{"class":250},[245,984,985],{"class":387}," ChainFeedback",[245,987,391],{"class":254},[245,989,990],{"class":387},"LoggingFeedback",[245,992,396],{"class":254},[245,994,995,997,999,1002,1004,1007,1009,1012],{"class":98,"line":258},[245,996,401],{"class":250},[245,998,404],{"class":307},[245,1000,1001],{"class":254},"(self, steps, dialog",[245,1003,271],{"class":250},[245,1005,1006],{"class":307},"None",[245,1008,727],{"class":254},[245,1010,1011],{"class":250},"**",[245,1013,1014],{"class":254},"kwargs):\n",[245,1016,1017,1019,1021,1023,1025,1027],{"class":98,"line":265},[245,1018,427],{"class":307},[245,1020,430],{"class":254},[245,1022,433],{"class":307},[245,1024,391],{"class":254},[245,1026,1011],{"class":250},[245,1028,1029],{"class":254},"kwargs)\n",[245,1031,1032,1034,1037,1039],{"class":98,"line":284},[245,1033,441],{"class":307},[245,1035,1036],{"class":254},".steps ",[245,1038,271],{"class":250},[245,1040,1041],{"class":254}," steps\n",[245,1043,1044,1046,1049,1051],{"class":98,"line":299},[245,1045,441],{"class":307},[245,1047,1048],{"class":254},".step ",[245,1050,271],{"class":250},[245,1052,1053],{"class":307}," 0\n",[245,1055,1056,1058,1061,1063],{"class":98,"line":312},[245,1057,441],{"class":307},[245,1059,1060],{"class":254},".dialog ",[245,1062,271],{"class":250},[245,1064,1065],{"class":254}," dialog\n",[245,1067,1068],{"class":98,"line":325},[245,1069,262],{"emptyLinePlaceholder":261},[245,1071,1072,1074,1077],{"class":98,"line":331},[245,1073,401],{"class":250},[245,1075,1076],{"class":387}," next_step",[245,1078,1079],{"class":254},"(self, name):\n",[245,1081,1082,1084,1086,1089],{"class":98,"line":464},[245,1083,441],{"class":307},[245,1085,1048],{"class":254},[245,1087,1088],{"class":250},"+=",[245,1090,1091],{"class":307}," 1\n",[245,1093,1094,1096,1099,1101,1104,1107,1110,1112,1115,1117,1120,1122,1125,1127,1130,1132,1134],{"class":98,"line":477},[245,1095,441],{"class":307},[245,1097,1098],{"class":254},".pushInfo(",[245,1100,786],{"class":250},[245,1102,1103],{"class":277},"\"[",[245,1105,1106],{"class":307},"{self",[245,1108,1109],{"class":254},".step",[245,1111,798],{"class":307},[245,1113,1114],{"class":277},"\u002F",[245,1116,1106],{"class":307},[245,1118,1119],{"class":254},".steps",[245,1121,798],{"class":307},[245,1123,1124],{"class":277},"] ",[245,1126,804],{"class":307},[245,1128,1129],{"class":254},"name",[245,1131,798],{"class":307},[245,1133,789],{"class":277},[245,1135,692],{"class":254},[245,1137,1138],{"class":98,"line":489},[245,1139,262],{"emptyLinePlaceholder":261},[245,1141,1142,1144,1146],{"class":98,"line":494},[245,1143,401],{"class":250},[245,1145,627],{"class":387},[245,1147,630],{"class":254},[245,1149,1150,1153,1155,1158,1160,1162,1165,1168,1171,1174,1177,1179,1182,1184,1186,1188,1190,1193],{"class":98,"line":505},[245,1151,1152],{"class":254},"        overall ",[245,1154,271],{"class":250},[245,1156,1157],{"class":254}," ((",[245,1159,511],{"class":307},[245,1161,1048],{"class":254},[245,1163,1164],{"class":250},"-",[245,1166,1167],{"class":307}," 1",[245,1169,1170],{"class":254},") ",[245,1172,1173],{"class":250},"+",[245,1175,1176],{"class":254}," progress ",[245,1178,1114],{"class":250},[245,1180,1181],{"class":307}," 100.0",[245,1183,1170],{"class":254},[245,1185,1114],{"class":250},[245,1187,599],{"class":307},[245,1189,1036],{"class":254},[245,1191,1192],{"class":250},"*",[245,1194,1195],{"class":307}," 100.0\n",[245,1197,1198,1200,1202],{"class":98,"line":517},[245,1199,596],{"class":250},[245,1201,599],{"class":307},[245,1203,1204],{"class":254},".dialog:\n",[245,1206,1207,1210,1213,1216],{"class":98,"line":522},[245,1208,1209],{"class":307},"            self",[245,1211,1212],{"class":254},".dialog.setValue(",[245,1214,1215],{"class":307},"int",[245,1217,1218],{"class":254},"(overall))\n",[245,1220,1221,1223],{"class":98,"line":533},[245,1222,427],{"class":307},[245,1224,1225],{"class":254},"().setProgress(overall)\n",[14,1227,1228,1230,1231,1234,1235,1238,1239,1242,1243,221],{},[207,1229,342],{}," Each algorithm reports its own progress from 0 to 100, so a chain needs to fold those into an overall figure — which is what the arithmetic here does. Feeding it into a ",[17,1232,1233],{},"QProgressBar"," gives a bar that moves smoothly across the whole job rather than resetting eight times. ",[17,1236,1237],{},"isCanceled()"," on the feedback is checked by well-behaved algorithms, so calling ",[17,1240,1241],{},"feedback.cancel()"," from a button handler stops the current step and the loop can then break out, exactly as described in ",[25,1244,1246],{"href":1245},"\u002Fqgis-plugin-development\u002Fbackground-tasks-and-plugin-performance\u002Fshow-plugin-progress-and-cancellation-pyqgis\u002F","showing plugin progress and cancellation",[196,1248,1250],{"id":1249},"a-runner-that-wraps-the-whole-pattern","A runner that wraps the whole pattern",[14,1252,1253],{},"Rather than repeating the try\u002Fexcept and the message checks at every step, wrap them once.",[236,1255,1257],{"className":238,"code":1256,"language":240,"meta":241,"style":241},"import processing\nfrom qgis.core import QgsProcessingException\n\n\ndef run_step(alg_id, params, feedback, label=None, strict_warnings=False):\n    label = label or alg_id\n    feedback.pushInfo(f\"→ {label}\")\n    before_errors = len(feedback.errors)\n    before_warnings = len(feedback.warnings)\n\n    try:\n        result = processing.run(alg_id, params, feedback=feedback)\n    except QgsProcessingException as exc:\n        feedback.reportError(f\"{label} failed: {exc}\", fatalError=True)\n        raise\n\n    new_errors = feedback.errors[before_errors:]\n    new_warnings = feedback.warnings[before_warnings:]\n    if new_errors:\n        raise RuntimeError(f\"{label} reported {len(new_errors)} error(s): {new_errors[0]}\")\n    if new_warnings and strict_warnings:\n        raise RuntimeError(f\"{label} reported {len(new_warnings)} warning(s): {new_warnings[0]}\")\n    if new_warnings:\n        feedback.pushInfo(f\"  {len(new_warnings)} warning(s) from {label}\")\n    return result\n",[17,1258,1259,1265,1276,1280,1284,1308,1324,1345,1358,1370,1374,1382,1400,1414,1452,1457,1461,1471,1481,1489,1535,1548,1593,1600,1629],{"__ignoreMap":241},[245,1260,1261,1263],{"class":98,"line":247},[245,1262,251],{"class":250},[245,1264,255],{"class":254},[245,1266,1267,1269,1271,1273],{"class":98,"line":258},[245,1268,363],{"class":250},[245,1270,366],{"class":254},[245,1272,251],{"class":250},[245,1274,1275],{"class":254}," QgsProcessingException\n",[245,1277,1278],{"class":98,"line":265},[245,1279,262],{"emptyLinePlaceholder":261},[245,1281,1282],{"class":98,"line":284},[245,1283,262],{"emptyLinePlaceholder":261},[245,1285,1286,1289,1292,1295,1297,1299,1302,1304,1306],{"class":98,"line":299},[245,1287,1288],{"class":250},"def",[245,1290,1291],{"class":387}," run_step",[245,1293,1294],{"class":254},"(alg_id, params, feedback, label",[245,1296,271],{"class":250},[245,1298,1006],{"class":307},[245,1300,1301],{"class":254},", strict_warnings",[245,1303,271],{"class":250},[245,1305,420],{"class":307},[245,1307,396],{"class":254},[245,1309,1310,1313,1315,1318,1321],{"class":98,"line":312},[245,1311,1312],{"class":254},"    label ",[245,1314,271],{"class":250},[245,1316,1317],{"class":254}," label ",[245,1319,1320],{"class":250},"or",[245,1322,1323],{"class":254}," alg_id\n",[245,1325,1326,1329,1331,1334,1336,1339,1341,1343],{"class":98,"line":325},[245,1327,1328],{"class":254},"    feedback.pushInfo(",[245,1330,786],{"class":250},[245,1332,1333],{"class":277},"\"→ ",[245,1335,804],{"class":307},[245,1337,1338],{"class":254},"label",[245,1340,798],{"class":307},[245,1342,789],{"class":277},[245,1344,692],{"class":254},[245,1346,1347,1350,1352,1355],{"class":98,"line":331},[245,1348,1349],{"class":254},"    before_errors ",[245,1351,271],{"class":250},[245,1353,1354],{"class":307}," len",[245,1356,1357],{"class":254},"(feedback.errors)\n",[245,1359,1360,1363,1365,1367],{"class":98,"line":464},[245,1361,1362],{"class":254},"    before_warnings ",[245,1364,271],{"class":250},[245,1366,1354],{"class":307},[245,1368,1369],{"class":254},"(feedback.warnings)\n",[245,1371,1372],{"class":98,"line":477},[245,1373,262],{"emptyLinePlaceholder":261},[245,1375,1376,1379],{"class":98,"line":489},[245,1377,1378],{"class":250},"    try",[245,1380,1381],{"class":254},":\n",[245,1383,1384,1387,1389,1392,1395,1397],{"class":98,"line":494},[245,1385,1386],{"class":254},"        result ",[245,1388,271],{"class":250},[245,1390,1391],{"class":254}," processing.run(alg_id, params, ",[245,1393,1394],{"class":683},"feedback",[245,1396,271],{"class":250},[245,1398,1399],{"class":254},"feedback)\n",[245,1401,1402,1405,1408,1411],{"class":98,"line":505},[245,1403,1404],{"class":250},"    except",[245,1406,1407],{"class":254}," QgsProcessingException ",[245,1409,1410],{"class":250},"as",[245,1412,1413],{"class":254}," exc:\n",[245,1415,1416,1419,1421,1423,1425,1427,1429,1432,1434,1437,1439,1441,1443,1445,1447,1450],{"class":98,"line":517},[245,1417,1418],{"class":254},"        feedback.reportError(",[245,1420,786],{"class":250},[245,1422,789],{"class":277},[245,1424,804],{"class":307},[245,1426,1338],{"class":254},[245,1428,798],{"class":307},[245,1430,1431],{"class":277}," failed: ",[245,1433,804],{"class":307},[245,1435,1436],{"class":254},"exc",[245,1438,798],{"class":307},[245,1440,789],{"class":277},[245,1442,727],{"class":254},[245,1444,649],{"class":683},[245,1446,271],{"class":250},[245,1448,1449],{"class":307},"True",[245,1451,692],{"class":254},[245,1453,1454],{"class":98,"line":522},[245,1455,1456],{"class":250},"        raise\n",[245,1458,1459],{"class":98,"line":533},[245,1460,262],{"emptyLinePlaceholder":261},[245,1462,1463,1466,1468],{"class":98,"line":541},[245,1464,1465],{"class":254},"    new_errors ",[245,1467,271],{"class":250},[245,1469,1470],{"class":254}," feedback.errors[before_errors:]\n",[245,1472,1473,1476,1478],{"class":98,"line":552},[245,1474,1475],{"class":254},"    new_warnings ",[245,1477,271],{"class":250},[245,1479,1480],{"class":254}," feedback.warnings[before_warnings:]\n",[245,1482,1483,1486],{"class":98,"line":557},[245,1484,1485],{"class":250},"    if",[245,1487,1488],{"class":254}," new_errors:\n",[245,1490,1491,1494,1496,1498,1500,1502,1504,1506,1508,1511,1513,1516,1518,1520,1522,1525,1527,1529,1531,1533],{"class":98,"line":574},[245,1492,1493],{"class":250},"        raise",[245,1495,611],{"class":307},[245,1497,391],{"class":254},[245,1499,786],{"class":250},[245,1501,789],{"class":277},[245,1503,804],{"class":307},[245,1505,1338],{"class":254},[245,1507,798],{"class":307},[245,1509,1510],{"class":277}," reported ",[245,1512,792],{"class":307},[245,1514,1515],{"class":254},"(new_errors)",[245,1517,798],{"class":307},[245,1519,801],{"class":277},[245,1521,804],{"class":307},[245,1523,1524],{"class":254},"new_errors[",[245,1526,50],{"class":307},[245,1528,812],{"class":254},[245,1530,798],{"class":307},[245,1532,789],{"class":277},[245,1534,692],{"class":254},[245,1536,1537,1539,1542,1545],{"class":98,"line":582},[245,1538,1485],{"class":250},[245,1540,1541],{"class":254}," new_warnings ",[245,1543,1544],{"class":250},"and",[245,1546,1547],{"class":254}," strict_warnings:\n",[245,1549,1550,1552,1554,1556,1558,1560,1562,1564,1566,1568,1570,1573,1575,1578,1580,1583,1585,1587,1589,1591],{"class":98,"line":593},[245,1551,1493],{"class":250},[245,1553,611],{"class":307},[245,1555,391],{"class":254},[245,1557,786],{"class":250},[245,1559,789],{"class":277},[245,1561,804],{"class":307},[245,1563,1338],{"class":254},[245,1565,798],{"class":307},[245,1567,1510],{"class":277},[245,1569,792],{"class":307},[245,1571,1572],{"class":254},"(new_warnings)",[245,1574,798],{"class":307},[245,1576,1577],{"class":277}," warning(s): ",[245,1579,804],{"class":307},[245,1581,1582],{"class":254},"new_warnings[",[245,1584,50],{"class":307},[245,1586,812],{"class":254},[245,1588,798],{"class":307},[245,1590,789],{"class":277},[245,1592,692],{"class":254},[245,1594,1595,1597],{"class":98,"line":605},[245,1596,1485],{"class":250},[245,1598,1599],{"class":254}," new_warnings:\n",[245,1601,1602,1605,1607,1610,1612,1614,1616,1619,1621,1623,1625,1627],{"class":98,"line":617},[245,1603,1604],{"class":254},"        feedback.pushInfo(",[245,1606,786],{"class":250},[245,1608,1609],{"class":277},"\"  ",[245,1611,792],{"class":307},[245,1613,1572],{"class":254},[245,1615,798],{"class":307},[245,1617,1618],{"class":277}," warning(s) from ",[245,1620,804],{"class":307},[245,1622,1338],{"class":254},[245,1624,798],{"class":307},[245,1626,789],{"class":277},[245,1628,692],{"class":254},[245,1630,1631,1634],{"class":98,"line":622},[245,1632,1633],{"class":250},"    return",[245,1635,1636],{"class":254}," result\n",[14,1638,1639,1641,1642,1645],{},[207,1640,342],{}," Slicing the message lists from a mark taken before the call is what attributes messages to the right step when a single feedback object is shared across a chain — without it, step five would re-report step two's warnings and fail forever. ",[17,1643,1644],{},"strict_warnings"," as a parameter rather than a constant lets one chain treat warnings as fatal while another logs them, which is usually the right split between a data-publishing pipeline and an exploratory one.",[14,1647,1648],{},"With this in place a chain reads as a list of steps and every one of them is instrumented identically, which is the property that makes a three-in-the-morning log actually readable. The same wrapper is a natural place to add timing, so the log records not only what happened but which step is the one worth optimising.",[196,1650,1652],{"id":1651},"failure-modes-worth-distinguishing","Failure modes worth distinguishing",[14,1654,1655],{},"Three quite different things all read as \"it did not work\", and the handling differs.",[14,1657,1658,1659,1662,1663,1665,1666,1669],{},"An ",[207,1660,1661],{},"invalid parameter"," — a missing file, a field that does not exist, a value out of range — raises ",[17,1664,233],{}," from ",[17,1667,1668],{},"run()"," before the algorithm starts. This is a programming or configuration error and should propagate.",[14,1671,1658,1672,1675],{},[207,1673,1674],{},"algorithm failure"," during execution also raises, but the message is usually from the underlying library — GDAL, GEOS, SAGA — and is worth logging verbatim rather than wrapped, because the original text is what a search engine will match.",[14,1677,1678,1679,1682],{},"A ",[207,1680,1681],{},"partial success"," does not raise at all: features skipped, geometries repaired, values clamped. Only the feedback sees these, which is the entire argument for always passing one.",[236,1684,1686],{"className":238,"code":1685,"language":240,"meta":241,"style":241},"from qgis.core import QgsProcessingException\n\ntry:\n    result = processing.run(alg_id, params, feedback=feedback)\nexcept QgsProcessingException as exc:\n    feedback.reportError(f\"{alg_id} failed: {exc}\", fatalError=True)\n    raise\n",[17,1687,1688,1698,1702,1709,1724,1735,1771],{"__ignoreMap":241},[245,1689,1690,1692,1694,1696],{"class":98,"line":247},[245,1691,363],{"class":250},[245,1693,366],{"class":254},[245,1695,251],{"class":250},[245,1697,1275],{"class":254},[245,1699,1700],{"class":98,"line":258},[245,1701,262],{"emptyLinePlaceholder":261},[245,1703,1704,1707],{"class":98,"line":265},[245,1705,1706],{"class":250},"try",[245,1708,1381],{"class":254},[245,1710,1711,1714,1716,1718,1720,1722],{"class":98,"line":284},[245,1712,1713],{"class":254},"    result ",[245,1715,271],{"class":250},[245,1717,1391],{"class":254},[245,1719,1394],{"class":683},[245,1721,271],{"class":250},[245,1723,1399],{"class":254},[245,1725,1726,1729,1731,1733],{"class":98,"line":299},[245,1727,1728],{"class":250},"except",[245,1730,1407],{"class":254},[245,1732,1410],{"class":250},[245,1734,1413],{"class":254},[245,1736,1737,1740,1742,1744,1746,1749,1751,1753,1755,1757,1759,1761,1763,1765,1767,1769],{"class":98,"line":312},[245,1738,1739],{"class":254},"    feedback.reportError(",[245,1741,786],{"class":250},[245,1743,789],{"class":277},[245,1745,804],{"class":307},[245,1747,1748],{"class":254},"alg_id",[245,1750,798],{"class":307},[245,1752,1431],{"class":277},[245,1754,804],{"class":307},[245,1756,1436],{"class":254},[245,1758,798],{"class":307},[245,1760,789],{"class":277},[245,1762,727],{"class":254},[245,1764,649],{"class":683},[245,1766,271],{"class":250},[245,1768,1449],{"class":307},[245,1770,692],{"class":254},[245,1772,1773],{"class":98,"line":325},[245,1774,1775],{"class":250},"    raise\n",[14,1777,1778,1780,1781,1783,1784,1787],{},[207,1779,342],{}," Re-raising after logging keeps the traceback intact while ensuring the failure also lands in whatever log the feedback writes to. Catching ",[17,1782,182],{}," broadly here is a mistake worth avoiding: a ",[17,1785,1786],{},"KeyboardInterrupt"," or a genuine bug in your own callback should not be reported as an algorithm failure.",[196,1789,1791],{"id":1790},"loading-results-and-when-not-to","Loading results, and when not to",[14,1793,1794,1797],{},[17,1795,1796],{},"processing.runAndLoadResults()"," runs the algorithm and adds its outputs to the project. It is convenient interactively and wrong in a batch.",[236,1799,1801],{"className":238,"code":1800,"language":240,"meta":241,"style":241},"import processing\n\nprocessing.runAndLoadResults(\"native:buffer\", {\n    \"INPUT\": \"\u002Fdata\u002Fparcels.gpkg\", \"DISTANCE\": 10,\n    \"OUTPUT\": \"\u002Fdata\u002Foutput\u002Fbuffered.gpkg\",\n})\n",[17,1802,1803,1809,1813,1822,1840,1850],{"__ignoreMap":241},[245,1804,1805,1807],{"class":98,"line":247},[245,1806,251],{"class":250},[245,1808,255],{"class":254},[245,1810,1811],{"class":98,"line":258},[245,1812,262],{"emptyLinePlaceholder":261},[245,1814,1815,1818,1820],{"class":98,"line":265},[245,1816,1817],{"class":254},"processing.runAndLoadResults(",[245,1819,278],{"class":277},[245,1821,281],{"class":254},[245,1823,1824,1826,1828,1830,1832,1834,1836,1838],{"class":98,"line":284},[245,1825,287],{"class":277},[245,1827,290],{"class":254},[245,1829,293],{"class":277},[245,1831,727],{"class":254},[245,1833,730],{"class":277},[245,1835,290],{"class":254},[245,1837,121],{"class":307},[245,1839,296],{"class":254},[245,1841,1842,1844,1846,1848],{"class":98,"line":299},[245,1843,315],{"class":277},[245,1845,290],{"class":254},[245,1847,320],{"class":277},[245,1849,296],{"class":254},[245,1851,1852],{"class":98,"line":312},[245,1853,328],{"class":254},[14,1855,1856,1858,1859,1861],{},[207,1857,342],{}," In a headless script there is no project the user will look at, so loading every intermediate wastes memory and can hold file handles open, which on Windows prevents a later step from overwriting the file. It also requires the full application context, so it fails in some minimal standalone setups. Use ",[17,1860,1668],{}," everywhere except in an interactive console, and add the final output to the project explicitly if it belongs there.",[196,1863,1865],{"id":1864},"qgis-version-compatibility","QGIS version compatibility",[14,1867,1868,1869,1871],{},"The examples target ",[207,1870,209],{}," (Python 3.12).",[1873,1874,1875,1891],"table",{},[1876,1877,1878],"thead",{},[1879,1880,1881,1885,1888],"tr",{},[1882,1883,1884],"th",{},"QGIS version",[1882,1886,1887],{},"Python",[1882,1889,1890],{},"Notes",[1892,1893,1894,1916,1931,1946,1957],"tbody",{},[1879,1895,1896,1900,1903],{},[1897,1898,1899],"td",{},"3.16 LTR",[1897,1901,1902],{},"3.7",[1897,1904,1905,1907,1908,727,1911,727,1913,221],{},[17,1906,352],{}," with ",[17,1909,1910],{},"pushInfo",[17,1912,162],{},[17,1914,1915],{},"setProgress",[1879,1917,1918,1921,1924],{},[1897,1919,1920],{},"3.22 LTR",[1897,1922,1923],{},"3.9",[1897,1925,1926,1928,1929,221],{},[17,1927,148],{}," added as a distinct severity from ",[17,1930,1910],{},[1879,1932,1933,1936,1938],{},[1897,1934,1935],{},"3.28 LTR",[1897,1937,1923],{},[1897,1939,1940,1942,1943,1945],{},[17,1941,162],{}," gains the ",[17,1944,649],{}," argument in Python bindings.",[1879,1947,1948,1951,1954],{},[1897,1949,1950],{},"3.34 LTR",[1897,1952,1953],{},"3.12",[1897,1955,1956],{},"Baseline for this page.",[1879,1958,1959,1962,1964],{},[1897,1960,1961],{},"3.40+",[1897,1963,1953],{},[1897,1965,1966,1969],{},[17,1967,1968],{},"pushFormattedMessage"," available for structured log output.",[196,1971,1973],{"id":1972},"troubleshooting","Troubleshooting",[201,1975,1976,1982,1993,1999,2008,2019],{},[204,1977,1978,1981],{},[207,1979,1980],{},"The output has fewer features than the input and nothing said so."," A warning was discarded. Pass a feedback object.",[204,1983,1984,1989,1990,221],{},[207,1985,1986,1988],{},[17,1987,1668],{}," returned but the file is missing."," The algorithm reported a non-fatal error. Check ",[17,1991,1992],{},"feedback.errors",[204,1994,1995,1998],{},[207,1996,1997],{},"The progress bar jumps back to zero repeatedly."," Each algorithm reports 0–100 separately. Fold them into an overall figure.",[204,2000,2001,2004,2005,2007],{},[207,2002,2003],{},"Cancelling does nothing."," The running algorithm does not check ",[17,2006,1237],{},". Nothing can be done except waiting for the step to finish.",[204,2009,2010,2016,2017,221],{},[207,2011,2012,2015],{},[17,2013,2014],{},"runAndLoadResults"," fails headlessly."," It needs the full application and a project. Use ",[17,2018,1668],{},[204,2020,2021,2024],{},[207,2022,2023],{},"The error message is unhelpfully wrapped."," Log the original exception text verbatim as well as your own message.",[196,2026,2028],{"id":2027},"conclusion","Conclusion",[14,2030,2031,2032,2035],{},"Always pass a feedback object, subclass it to route messages to a real log, and decide explicitly whether a non-fatal error should stop the chain. Distinguish parameter errors from algorithm failures from partial successes, fold per-step progress into an overall figure, and keep ",[17,2033,2034],{},"runAndLoadResults()"," for the console.",[196,2037,2039],{"id":2038},"frequently-asked-questions","Frequently Asked Questions",[14,2041,2042,2045],{},[207,2043,2044],{},"Can I see the algorithm's command line?","\nFor GDAL and SAGA algorithms, yes — the console command appears as an info message on the feedback, which is invaluable when reproducing a failure outside QGIS.",[14,2047,2048,2051,2052,2054],{},[207,2049,2050],{},"How do I silence an algorithm entirely?","\nPass a feedback subclass whose methods do nothing. That is different from passing ",[17,2053,1006],{},", which uses a default instance and still writes to the Processing log in a GUI session.",[14,2056,2057,2064,2065,2068,2069,221],{},[207,2058,2059,2060,2063],{},"Does feedback work with ",[17,2061,2062],{},"qgis_process","?","\nThe command-line runner has its own reporting and writes messages to stdout, with ",[17,2066,2067],{},"--json"," for machine-readable output. See ",[25,2070,2072],{"href":2071},"\u002Fpyqgis-fundamentals-environment-setup\u002Fheadless-qgis-and-server-automation\u002Fuse-qgis-process-command-line-runner\u002F","using the qgis_process command line runner",[14,2074,2075,2078,2079,2082,2083,2086,2087,2090,2091,221],{},[207,2076,2077],{},"Can a custom algorithm push its own messages?","\nYes — inside ",[17,2080,2081],{},"processAlgorithm"," the feedback is passed in, and calling ",[17,2084,2085],{},"feedback.pushInfo()"," or ",[17,2088,2089],{},"feedback.reportError()"," makes your algorithm as observable as the built-in ones. See ",[25,2092,2094],{"href":2093},"\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002Freport-progress-and-cancel-processing-algorithm\u002F","reporting progress and cancellation in a processing algorithm",[196,2096,2098],{"id":2097},"related","Related",[201,2100,2101,2106,2112,2118,2124],{},[204,2102,2103,2105],{},[25,2104,28],{"href":27}," — the guide this recipe belongs to",[204,2107,2108],{},[25,2109,2111],{"href":2110},"\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Frun-graphical-model-from-python-pyqgis\u002F","Run a Graphical Model from Python in PyQGIS",[204,2113,2114],{},[25,2115,2117],{"href":2116},"\u002Fspatial-data-processing-automation\u002Fbatch-processing-with-pyqgis\u002Frun-processing-algorithm-from-script\u002F","Run a Processing Algorithm from a Script",[204,2119,2120],{},[25,2121,2123],{"href":2122},"\u002Fpyqgis-fundamentals-environment-setup\u002Fheadless-qgis-and-server-automation\u002Fhandle-errors-and-logging-in-unattended-scripts\u002F","Handle Errors and Logging in Unattended Scripts",[204,2125,2126],{},[25,2127,2128],{"href":1245},"Show Plugin Progress and Cancellation in PyQGIS",[2130,2131,2132],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":241,"searchDepth":258,"depth":258,"links":2134},[2135,2136,2137,2138,2139,2140,2141,2142,2143,2144,2145,2146,2147],{"id":198,"depth":258,"text":199},{"id":224,"depth":258,"text":225},{"id":346,"depth":258,"text":347},{"id":664,"depth":258,"text":665},{"id":969,"depth":258,"text":970},{"id":1249,"depth":258,"text":1250},{"id":1651,"depth":258,"text":1652},{"id":1790,"depth":258,"text":1791},{"id":1864,"depth":258,"text":1865},{"id":1972,"depth":258,"text":1973},{"id":2027,"depth":258,"text":2028},{"id":2038,"depth":258,"text":2039},{"id":2097,"depth":258,"text":2098},"Capture progress, warnings and failures from processing.run — QgsProcessingFeedback subclasses, why run does not always raise, processing.runAndLoadResults, and cancellable long jobs.","md",{"slug":2151,"type":2152,"breadcrumb":2153,"datePublished":2154,"dateModified":2154},"handle-processing-feedback-and-errors-pyqgis","article","Feedback & Errors","2026-08-27","\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Fhandle-processing-feedback-and-errors-pyqgis",{"title":5,"description":2148},"spatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Fhandle-processing-feedback-and-errors-pyqgis\u002Findex","wWlansDUfYBwTYhgADEvmWDEidlBw2HaUIxrCMnTj5M",1787823363554]